Opdracht 3: Bepalen van muziekgenres¶

Gemaakt door de Music Masters:

    Mark "Markolie20" Olieman
    Daan "Daan Eising" Eising
    Jesse "zdfgdfsg" van der Westen

Inhoudsopgave¶

  • Hoofdstuk 1: Importeren van Libraries en Modules

  • Hoofdstuk 2: Feature Engineering

    • 2.1: Feature engineering class aanmaken
    • 2.2: Feature engineering functies
    • 2.3: Toelichting features
    • 2.4: Dataframe scalen
  • Hoofdstuk 3: Unsupervised Learning

    • 3.1 Clustering
    • 3.2: Bepalen van Genres
    • 3.3: Principal Component Analysis
    • 3.4: Non-negative matrix factorization
    • 3.5: Dimensionality Reduction
    • 3.6: Aanbevelingsapp
  • Hoofdstuk 4: Bevindingen en Conclusie

    • 4.1 Bevindingen
    • 4.2 Conclusie
  • Hoofdstuk 5: Referentielijst

Hoofdstuk 1: Importeren van Libraries en Modules¶

In [6]:
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import glob
import warnings
import plotly.express as px
from pydub import AudioSegment

import IPython.display as ipd
from IPython.display import display
from IPython.display import Audio

import librosa as lr
from librosa import load as lr_load
from librosa import amplitude_to_db
from librosa.feature import melspectrogram, mfcc, chroma_stft, spectral_flatness, zero_crossing_rate, tempogram
from librosa.beat import tempo, beat_track
from librosa.feature import spectral_contrast as lf_spectral_contrast
from librosa.feature import spectral_rolloff as lf_spectral_rolloff
from librosa import pyin, amplitude_to_db

from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN, Birch, MeanShift
from sklearn.decomposition import PCA
from sklearn.decomposition import NMF
from sklearn.preprocessing import StandardScaler, MinMaxScaler

from scipy.io import wavfile
from scipy.spatial.distance import euclidean, cityblock

Hoofdstuk 2: Feature Engineering¶

2.1: Feature engineering class aanmaken¶

In [7]:
class AudioFeatureExtractor:
    """
    A class for extracting various audio features from an audio signal.

    Attributes:
        None

    Methods:
        mfccs(data, sfreq):
            Extracts Mel-Frequency Cepstral Coefficients (MFCCs) from the audio signal.

        calculate_spectrograms(audio_clips, n_fft=2048, hop_length=512, win_length=None):
            Calculates spectrograms from a list of audio clips.

        calculate_spectral_features(spectrograms):
            Calculates spectral features (bandwidth and centroid) from a list of spectrograms.

        calculate_spectral_contrast(data, sr, n_fft=2048, hop_length=512):
            Calculates spectral contrast from the audio signal.

        calculate_tonnetz(data, sr):
            Calculates tonnetz features from the audio signal.

        calculate_spectral_rolloff(data, sr, roll_percent=0.85, n_fft=2048, hop_length=512):
            Calculates spectral rolloff from the audio signal.

        calculate_chroma_features(data, sr, n_fft=2048, hop_length=512):
            Calculates chroma features from the audio signal.

        calculate_mel_spectral_contrast(data, sr, n_fft=2048, hop_length=512):
            Calculates mel spectrogram and its contrast from the audio signal.

        calculate_spectral_flatness(data, sr, n_fft=2048, hop_length=512):
            Calculates spectral flatness from the audio signal.

        zero_crossing_rate_features(data, n_fft=2048, hop_length=512):
            Calculates zero-crossing rate from the audio signal.

        rms_energy_features(data):
            Calculates root mean square (RMS) energy from the audio signal.
    """

    def __init__(self):
        pass

    def mfccs(self, data, sr):
        """
        Extracts Mel-Frequency Cepstral Coefficients (MFCCs) from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.

        Returns:
            dict: A dictionary containing the mean of each MFCC coefficient.
        """
        mfcc_result = mfcc(y=data, sr=sr)
        datadict = {}
        for var in range(len(mfcc_result)):
            datadict[f'mfcc{var + 1}_mean'] = np.mean(mfcc_result[var, :])
        return datadict

    def calculate_spectrograms(self, audio_clips, n_fft=2048, hop_length=512, win_length=None):
        """
        Calculates spectrograms from a list of audio clips.

        Parameters:
            audio_clips (list): List of audio clips.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.
            win_length (int): The length of the windowing segments.

        Returns:
            tuple: Two lists - spectrograms and their corresponding dB-scaled versions.
        """
        spectrograms = []
        spectrograms_db = []

        for clip in audio_clips:
            stft_matrix = lr.stft(y=clip, n_fft=n_fft, hop_length=hop_length, win_length=win_length)
            spectrogram = np.abs(stft_matrix)
            spec_db = amplitude_to_db(S=spectrogram, ref=np.max)
            spectrograms.append(spectrogram)
            spectrograms_db.append(spec_db)
        return spectrograms, spectrograms_db

    def calculate_spectral_features(self, spectrograms):
        """
        Calculates spectral features (bandwidth and centroid) from a list of spectrograms.

        Parameters:
            spectrograms (list): List of spectrograms.

        Returns:
            tuple: Two lists - spectral bandwidths and centroids.
        """
        bandwidths = []
        centroids = []

        for spectrogram in spectrograms:
            spec_bw = lr.feature.spectral_bandwidth(S=spectrogram)
            spec_cn = lr.feature.spectral_centroid(S=spectrogram)
            bandwidths.append(spec_bw)
            centroids.append(spec_cn)
        return bandwidths, centroids

    def calculate_spectral_contrast(self, data, sr, n_fft=2048, hop_length=512):
        """
        Calculates spectral contrast from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Spectral contrast features.
        """
        datadict = {}
        spectral_contrast = lr.feature.spectral_contrast(y=data, sr=sr, n_fft=n_fft, hop_length=hop_length)
        for var in range(len(spectral_contrast)):
            datadict[f'spectral_contrast{var + 1}_mean'] = np.mean(spectral_contrast[var, :])
        return datadict

    def calculate_tonnetz(self, data, sr):
        """
        Calculates tonnetz features from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.

        Returns:
            numpy.ndarray: Tonnetz features.
        """
        tonnetz = lr.feature.tonnetz(y=data, sr=sr)
        datadict = {}
        for var in range(len(tonnetz)):
            datadict[f'tonnetz{var + 1}_mean'] = np.mean(tonnetz[var, :])
        return datadict

    def calculate_spectral_rolloff(self, data, sr, roll_percent=0.85, n_fft=2048, hop_length=512):
        """
        Calculates spectral rolloff from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.
            roll_percent (float): Percentage for determining rolloff.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Spectral rolloff features.
        """
        spectral_rolloff = lr.feature.spectral_rolloff(y=data, sr=sr, roll_percent=roll_percent, n_fft=n_fft, hop_length=hop_length)
        return spectral_rolloff

    def calculate_chroma_features(self, data, sr, n_fft=2048, hop_length=512):
        """
        Calculates chroma features from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Chroma features.
        """
        chromagram = chroma_stft(y=data, sr=sr, n_fft=n_fft, hop_length=hop_length)
        datadict = {}
        for var in range(len(chromagram)):
            datadict[f'chromagram{var + 1}_mean'] = np.mean(chromagram[var, :])
        return datadict

    def calculate_mel_spectral_contrast(self, data, sr, n_fft=2048, hop_length=512):
        """
        Calculates mel spectrogram and its contrast from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Mel spectrogram contrast features.
        """
        mel_spectrogram = melspectrogram(y=data, sr=sr, n_fft=n_fft, hop_length=hop_length)
        mel_spectral_contrast = lr.feature.spectral_contrast(S=mel_spectrogram)
        datadict = {}
        for var in range(len(mel_spectral_contrast)):
            datadict[f'mel_spectral_contrast{var + 1}_mean'] = np.mean(mel_spectral_contrast[var, :])
        return datadict

    def calculate_spectral_flatness(self, data, sr, n_fft=2048, hop_length=512):
        """
        Calculates spectral flatness from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            sr (int): The sampling rate of the audio signal.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Spectral flatness features.
        """
        flatness = lr.feature.spectral_flatness(y=data, n_fft=n_fft, hop_length=hop_length)
        return flatness

    def zero_crossing_rate_features(self, data, n_fft=2048, hop_length=512):
        """
        Calculates zero-crossing rate from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.
            n_fft (int): The number of points for each FFT.
            hop_length (int): The number of samples between successive frames.

        Returns:
            numpy.ndarray: Zero-crossing rate features.
        """
        zcr = zero_crossing_rate(y=data, frame_length=n_fft, hop_length=hop_length)
        return zcr

    def rms_energy_features(self, data):
        """
        Calculates root mean square (RMS) energy from the audio signal.

        Parameters:
            data (numpy.ndarray): The audio signal.

        Returns:
            numpy.ndarray: RMS energy features.
        """
        rms_energy = lr.feature.rms(y=data)[0]
        return rms_energy

2.2: Feature engineering functies¶

In [8]:
csv_path = "labels_new.csv"
audio_directory_labeled = "labeled"
audio_directory_unlabeled = "unlabeled"

audio_extractor = AudioFeatureExtractor()
# Read the CSV file into a DataFrame
df = pd.read_csv(csv_path)

def process_audio_features(audio_directory, labeled=True, df=df):
    # Audio features storage
    audio_features_list = []

    # Set the target length for audio truncation or padding
    lengte = 30 * 22050

    audio_arrays = np.empty((len(os.listdir(audio_directory)), lengte))
    # Process each audio file
    for index, file in enumerate(os.listdir(audio_directory)):
        sr = lr.get_samplerate(f'{audio_directory}/{file}')
        if file.endswith(".wav"):
            # sr = lr.get_samplerate(file)
            file_path = os.path.join(audio_directory, file)
            data, sfreq = lr.load(file_path, sr=None)
            # Truncate or pad the audio
            if len(data) > lengte:
                # Truncate the data
                data = data[:lengte]
            elif len(data) < lengte:
                # Pad with zeros
                padding = lengte - len(data)
                data = np.pad(data, (0, padding), mode='constant')
            
            audio_arrays[index] = data
            
            # Extract MFCC features
            mfcc_features = audio_extractor.mfccs(data, sfreq)
            spectral_contrast_features = audio_extractor.calculate_spectral_contrast(data, sr)
            tonnetz_features = audio_extractor.calculate_tonnetz(data, sr)
            spectral_rolloff_feature = audio_extractor.calculate_spectral_rolloff(data, sr)
            chroma_features = audio_extractor.calculate_chroma_features(data, sr)
            mel_spectral_contrast_features = audio_extractor.calculate_mel_spectral_contrast(data, sr)
            spectral_flatness_feature = audio_extractor.calculate_spectral_flatness(data, sr)
            zero_crossing_feature = audio_extractor.zero_crossing_rate_features(data)
            rms_energy = audio_extractor.rms_energy_features(data)
            
       # Combine all features into a dictionary
            features_dict = {
                'filename': file, 
                **mfcc_features,
                'rms_energy': np.mean(rms_energy),
                **spectral_contrast_features,
                **tonnetz_features,
                'spectral_rolloff': np.mean(spectral_rolloff_feature[0]),
                **chroma_features,
                **mel_spectral_contrast_features,
                'spectral_flatness': np.mean(spectral_flatness_feature[0]),
                'zcr': np.mean(zero_crossing_feature[0])
            }
                # Append the features dictionary to the list
            audio_features_list.append(features_dict) 
            
    spectograms, spectogram_features = audio_extractor.calculate_spectrograms(audio_arrays)
    bandwidths, centroids = audio_extractor.calculate_spectral_features(spectograms)

    bandwiths = pd.DataFrame({'bandwith': [np.mean(bandwidth[0]) for bandwidth in bandwidths]})
    centroids = pd.DataFrame({'centroid': [np.mean(centroid[0]) for centroid in centroids]})
    # Convert the list of feature dictionaries to a DataFrame
    audio_features_df = pd.DataFrame(audio_features_list)
    audio_features_df = pd.concat([audio_features_df, bandwiths], axis=1)
    audio_features_df = pd.concat([audio_features_df, centroids], axis=1)
    # Merge the original DataFrame with the new features DataFrame based on the 'filename' column
    if labeled:
        audio_features_df = pd.merge(df, audio_features_df, on='filename', how='left')

    audio_features_df = audio_features_df.sort_values(by='filename')
    return audio_features_df

# Process audio features
labeled_df = process_audio_features(audio_directory_labeled)
display(labeled_df)
filename genre mfcc1_mean mfcc2_mean mfcc3_mean mfcc4_mean mfcc5_mean mfcc6_mean mfcc7_mean mfcc8_mean ... mel_spectral_contrast2_mean mel_spectral_contrast3_mean mel_spectral_contrast4_mean mel_spectral_contrast5_mean mel_spectral_contrast6_mean mel_spectral_contrast7_mean spectral_flatness zcr bandwith centroid
8 m00002.wav jazz -298.807953 112.078209 6.485770 28.386517 -6.764679 16.651894 -11.809684 12.746783 ... 6.739004 17.720329 31.856210 37.582171 36.234603 42.188262 0.001252 0.051222 1919.917650 1451.498371
9 m00039.wav reggae -169.243668 110.447716 -8.553957 43.898693 0.266454 26.646509 -14.365674 13.026835 ... 8.001072 15.869485 22.225069 29.004593 27.931632 43.075836 0.004306 0.072178 2019.252686 1811.358216
43 m00041.wav pop -18.854591 71.328522 -3.743232 -1.396592 0.710347 -1.049137 -1.052407 -0.249471 ... 5.718698 13.057337 17.657730 23.827369 27.071292 18.570913 0.078506 0.152910 2992.192112 3111.061099
29 m00072.wav disco -69.599335 83.059570 -16.599524 0.119469 7.415704 0.769619 1.337008 4.690686 ... 6.370224 12.197484 18.810620 25.720595 24.686535 23.377385 0.038270 0.120259 2709.990169 2625.095044
34 m00096.wav disco -91.886307 87.604057 -2.058175 34.285538 -18.153370 19.344702 -14.697328 17.116173 ... 4.989545 15.582052 20.384724 25.294628 29.771765 46.579813 0.009796 0.115890 2486.020650 2550.135384
48 m00102.wav classical -436.842285 154.113205 -19.859337 20.183603 3.164744 0.446392 -6.530639 -4.603339 ... 8.704671 18.088766 26.164080 36.452103 37.462534 35.556624 0.000976 0.066325 1517.856160 1173.931066
25 m00112.wav reggae -265.694977 80.615921 9.722022 36.657085 27.836796 12.086828 15.014478 15.116937 ... 3.927251 15.154408 20.473471 26.257647 25.445807 24.486615 0.026549 0.060883 2595.217468 2113.391953
4 m00138.wav reggae -198.632797 102.413582 -10.031449 30.802383 -3.310606 20.276924 -5.637373 28.095631 ... 8.111595 11.985461 21.690643 26.647311 26.212014 47.129611 0.005168 0.070323 2295.466240 1995.600514
5 m00192.wav classical -85.264992 118.423058 -31.472771 20.282015 -4.417709 6.922598 -10.950942 -1.620085 ... 5.707315 9.457773 16.339001 23.798978 26.500622 37.537164 0.010484 0.111613 1965.028715 1878.774030
22 m00206.wav hiphop -92.591652 77.412682 -32.756371 54.596119 -13.199195 33.656281 -12.085772 28.015388 ... 3.856905 11.629413 12.605242 16.697020 17.101189 52.441041 0.018014 0.157603 2295.456105 2735.795802
1 m00230.wav country -89.736382 36.286205 11.469535 37.495590 9.199136 3.740519 2.840358 6.811540 ... 4.663522 12.311560 17.618104 28.147029 26.801658 22.182678 0.093471 0.212375 3053.066772 3760.424488
47 m00236.wav classical -368.465942 124.265991 -31.582445 17.601763 2.322930 -5.949597 -11.807772 -5.547481 ... 4.495887 16.020693 30.754881 34.882406 36.114791 54.128590 0.000620 0.078817 1415.453381 1329.264956
0 m00248.wav metal -75.517509 81.911423 -22.081079 69.876999 -11.740438 25.740246 -18.518965 27.027710 ... 2.714730 9.285961 13.318006 15.142012 17.020385 57.419416 0.018547 0.158776 2337.227557 2656.165734
45 m00253.wav blues -3.559072 92.927788 -25.118544 45.187210 -10.871894 31.862423 -18.057922 28.628012 ... 3.783532 12.969313 16.648841 21.717000 24.115583 57.660211 0.010716 0.124735 2358.384492 2443.238916
40 m00298.wav blues -213.204529 115.154541 -11.720503 39.032555 -20.357819 13.089926 -9.180815 9.023639 ... 5.069766 14.647256 24.722086 30.720819 27.451555 51.615333 0.002710 0.079215 1973.690309 1817.529547
27 m00313.wav blues -249.289398 130.987030 0.677390 66.685783 17.897806 4.623737 6.598308 1.249050 ... 4.712652 16.387888 25.459972 31.543672 29.853833 51.528179 0.000401 0.051725 1463.539070 1109.721379
39 m00338.wav blues -55.596630 114.951218 -37.069351 64.904381 -7.164943 15.254702 -16.235331 18.643576 ... 3.855680 11.104764 19.421730 24.030727 23.153801 59.228233 0.004599 0.106627 1927.439014 1977.108904
44 m00339.wav rock -70.117538 94.181656 -50.328033 46.962898 -16.114254 19.996185 -23.959724 16.344028 ... 3.767756 8.777708 17.115519 22.762585 21.009458 66.166621 0.014635 0.150945 2102.197363 2471.302264
32 m00351.wav jazz -192.517395 113.183693 -6.373058 39.593094 -9.189579 17.204056 -19.114599 12.203582 ... 4.895479 13.100308 18.168636 31.750680 30.777813 52.074700 0.003388 0.076921 2107.923976 1825.975175
16 m00400.wav blues -350.358887 169.546326 31.780231 16.713755 28.661758 19.249090 7.846700 10.039796 ... 6.831016 15.706191 25.741073 31.403125 30.358803 22.970648 0.000457 0.021701 995.093473 570.150688
13 m00421.wav pop -77.941887 32.364956 20.077257 27.633484 16.262774 5.319164 13.481539 3.503227 ... 9.366871 17.067590 22.242897 24.953492 25.764494 21.049781 0.066714 0.144787 3223.867461 3727.248835
6 m00429.wav hiphop -109.509178 97.389557 -20.619621 37.366669 -3.125998 28.611204 -14.169424 23.862110 ... 4.376860 8.659314 15.042835 21.151530 22.051505 50.071179 0.011507 0.104502 2327.237804 2272.834439
46 m00435.wav classical -207.377472 117.607780 -41.404255 13.253087 -1.688240 15.532319 -10.362769 -1.863690 ... 4.804461 20.265242 22.574447 30.592474 40.035231 56.377036 0.000801 0.108258 1551.481235 1623.829998
35 m00454.wav hiphop -103.138268 74.090218 -22.434641 51.604790 -7.079813 32.670975 -14.993524 19.642502 ... 5.691232 11.065545 15.903900 23.839271 22.870837 53.006697 0.016491 0.117624 2396.648334 2670.154564
49 m00477.wav classical -412.959259 138.519836 -19.262381 34.146679 8.975063 -4.294471 4.152584 0.881950 ... 5.582785 20.293131 30.609275 42.342264 38.614245 38.829502 0.000672 0.060885 1467.205561 1171.844481
14 m00501.wav jazz -168.123230 108.271530 -14.149940 30.457626 3.894506 4.676564 3.187918 1.927635 ... 5.479137 19.986478 27.292205 31.991843 30.909894 30.390149 0.006512 0.067522 2091.113741 1725.634530
12 m00503.wav metal -93.591331 89.894020 -55.902695 51.637325 -5.558396 28.777815 -13.892873 27.350773 ... 6.208451 8.601995 15.799067 22.311950 21.688064 62.071454 0.012679 0.151809 2037.300179 2463.449458
42 m00513.wav pop -52.649151 47.782742 13.455156 2.213427 13.251046 9.133875 14.447654 0.873837 ... 7.743124 10.976887 14.634204 21.740248 26.191926 20.857168 0.097539 0.210715 3426.219520 4052.888670
36 m00553.wav disco -95.103706 109.353172 -30.161179 16.358656 20.039730 11.962816 3.485530 6.173969 ... 4.868121 11.508925 16.536470 23.614719 23.721108 29.510298 0.026017 0.103663 2143.899287 2117.550762
33 m00606.wav country -171.339005 137.354568 7.964579 28.539564 2.708197 -0.501510 -2.956100 -1.159342 ... 6.591413 10.533838 16.756754 25.936614 26.975167 28.856986 0.007312 0.063856 1959.987665 1469.784950
7 m00623.wav reggae -94.140526 68.229942 3.975480 7.631387 12.791034 -3.185735 -3.907444 3.817600 ... 7.047003 16.691960 18.115008 23.019129 24.171044 21.322361 0.065537 0.136075 2974.065401 3086.186001
3 m00627.wav metal -57.683388 101.432327 -41.485245 55.130600 -23.349279 28.151102 -12.139105 18.150204 ... 4.042038 9.291089 13.214748 17.212025 17.637471 58.275039 0.012321 0.141317 2092.439557 2378.392048
17 m00629.wav country -82.962616 122.532806 -16.658556 40.015911 -15.007229 21.670378 -18.256283 18.361864 ... 5.854305 15.034976 18.742446 24.931771 25.955064 54.375683 0.005227 0.081104 2116.237657 1881.161818
28 m00633.wav country -122.427414 111.483139 -13.694294 53.828403 -2.677765 19.677549 -14.138923 13.743676 ... 5.908667 12.477974 21.097610 27.547413 27.157049 52.619927 0.005293 0.078317 2085.606346 1879.937791
2 m00637.wav hiphop -122.780525 95.061287 -29.363251 46.780045 -15.998561 27.117586 -13.113779 20.258003 ... 6.453961 10.992537 16.350120 21.788517 20.466351 50.293757 0.013041 0.113229 2181.589027 2290.107318
38 m00658.wav hiphop -105.569511 76.674911 -24.774305 55.198990 -8.257657 28.899265 -15.747581 26.304575 ... 3.862585 12.135674 13.885980 19.403479 20.607843 53.621895 0.013526 0.138555 2350.956242 2628.976734
21 m00671.wav reggae -251.392563 105.855774 2.220024 32.484390 16.563274 8.290815 10.365557 5.504937 ... 6.202627 13.453879 14.213644 19.654592 19.496502 29.640906 0.026613 0.089780 2298.145722 2004.353847
23 m00676.wav pop 9.675074 88.826073 -20.293680 5.101305 -2.917802 -0.188181 0.327482 1.604321 ... 3.538988 9.555049 13.832719 17.227212 19.715628 21.549278 0.064364 0.131656 2559.529362 2569.255823
10 m00677.wav country -139.623062 128.793533 -20.249359 49.443615 -2.160360 16.988024 -8.355296 9.818295 ... 7.767790 18.787435 27.903697 35.160599 34.218542 48.088884 0.001416 0.061745 1690.685707 1425.263509
20 m00678.wav metal -95.339104 92.066360 -28.061176 52.796284 -3.018975 25.395771 -6.135991 20.098093 ... 3.902639 12.254983 15.608343 18.157071 18.915123 47.654008 0.011364 0.115765 2115.725580 2241.454932
31 m00716.wav jazz -202.608383 105.845734 -20.537838 43.897964 -14.272349 25.299541 -15.592009 15.311663 ... 5.576754 14.930754 16.889706 26.349590 28.195721 51.507869 0.006012 0.093247 2084.706315 2034.579158
18 m00762.wav disco -155.637314 60.568497 23.166864 31.186348 25.054384 16.906994 9.438360 8.449133 ... 4.454963 14.698277 18.425711 22.050855 20.513930 20.602898 0.045092 0.101249 2990.754025 2909.983968
37 m00772.wav rock -125.065109 115.203308 -48.004681 52.843102 -13.491938 21.765408 -12.428561 20.139965 ... 5.201100 10.631122 18.387787 22.359313 22.331249 61.219149 0.007513 0.121824 1926.895810 2077.166788
26 m00773.wav pop -204.752975 101.605118 26.384224 9.185719 10.460928 5.108919 -9.447198 9.308316 ... 9.920602 17.031494 27.189178 34.895090 35.677321 28.118941 0.009270 0.075366 2559.593931 2053.894372
15 m00801.wav rock -212.298355 65.759850 45.182362 19.730082 11.805884 8.061574 4.434145 4.215858 ... 7.496245 14.080840 24.372299 29.930723 29.264457 19.750201 0.031043 0.108570 3203.015264 2947.109224
24 m00821.wav metal -58.720360 70.558182 -19.421354 73.593155 -27.765425 23.759441 -17.904169 21.222898 ... 3.810858 9.925774 14.813298 17.154393 16.498257 62.176253 0.025555 0.196540 2358.882414 3048.505206
41 m00850.wav disco -78.351181 80.074615 -9.167377 25.477053 -3.390544 16.809401 7.270706 1.818662 ... 3.839945 11.835582 16.459640 24.339468 26.110460 26.963877 0.040865 0.126389 2533.400412 2605.158779
11 m00867.wav rock -142.442062 116.238441 -32.190319 49.114605 -8.395415 22.872288 -18.311134 20.106487 ... 7.675207 11.516393 18.302119 25.070244 23.581773 56.706450 0.006455 0.097659 2068.224879 2006.009248
19 m00895.wav rock -26.406458 85.794289 -16.961445 35.260883 10.000134 10.668921 1.446997 4.710635 ... 4.893592 13.844285 19.658359 25.195343 25.510141 32.623636 0.027207 0.115493 2378.385172 2345.885737
30 m00996.wav jazz -235.678848 144.767654 -22.600695 62.560963 -9.326273 2.016869 -0.343650 2.016511 ... 4.571889 12.846922 19.813868 31.435092 32.057657 49.188174 0.001253 0.076529 1518.994776 1338.236233

50 rows × 60 columns

In [9]:
#aanpassen
import random
# Functie om een willekeurig muziekfragment af te spelen
def play_random_audio_with_info(audio_directory, labeled_df):
    # Lijst van audiobestanden in de opgegeven map
    audio_files = [file for file in os.listdir(audio_directory) if file.endswith(".wav")]
    # Selecteer een willekeurig audiobestand
    random_audio_file = random.choice(audio_files)
    # Bepaal het volledige pad naar het geselecteerde bestand
    audio_path = os.path.join(audio_directory, random_audio_file)
    
    # Haal het genre op basis van de bestandsnaam uit de labeled_df
    genre = labeled_df.loc[labeled_df['filename'] == random_audio_file, 'genre'].values[0]
    # Print de bestandsnaam en het genre
    print("Bestandsnaam:", random_audio_file)
    print("Genre:", genre)
    
    # Speel het audiobestand af
    display(Audio(audio_path))

# Speel een willekeurig muziekfragment af uit de gelabelde map
play_random_audio_with_info(audio_directory_labeled, labeled_df)
Bestandsnaam: m00477.wav
Genre: classical
Your browser does not support the audio element.
In [10]:
unlabeled_df = process_audio_features(audio_directory_unlabeled, labeled=False)
display(unlabeled_df)
filename mfcc1_mean mfcc2_mean mfcc3_mean mfcc4_mean mfcc5_mean mfcc6_mean mfcc7_mean mfcc8_mean mfcc9_mean ... mel_spectral_contrast2_mean mel_spectral_contrast3_mean mel_spectral_contrast4_mean mel_spectral_contrast5_mean mel_spectral_contrast6_mean mel_spectral_contrast7_mean spectral_flatness zcr bandwith centroid
0 m00003.wav -82.501259 97.344116 -34.373585 71.405922 -3.283182 17.367373 -9.023832 16.011181 -19.003857 ... 5.011253 13.892055 18.007974 21.183092 21.028454 58.034254 0.009073 0.120159 2070.908728 2254.451748
1 m00012.wav -1.925411 72.695557 -32.789642 63.592033 -18.557953 25.872955 -13.854105 18.064384 -11.945406 ... 6.322091 13.544996 16.113373 22.568365 18.241123 55.859046 0.026390 0.178119 2286.246872 2908.260266
2 m00013.wav -287.520996 101.737930 -35.368999 41.282764 -12.745123 17.284992 -13.978299 16.311886 -5.599029 ... 4.346955 16.482222 24.210026 32.560252 34.102601 59.447519 0.002209 0.123492 1865.851544 1953.012399
3 m00043.wav -120.127808 91.287666 -38.794960 75.003784 -3.806012 22.666491 -8.123723 23.654572 -14.638494 ... 4.574703 13.135090 15.795038 18.074718 19.134837 60.384606 0.008840 0.126103 2073.302486 2384.812610
4 m00044.wav -437.525208 170.971405 7.386171 -3.108705 0.777302 -11.135056 -12.523807 -6.900928 -5.850053 ... 4.602050 18.591778 23.050981 33.758989 36.850383 40.763202 0.000153 0.052112 899.979779 790.478225
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
100 m00971.wav -246.218369 149.443878 -41.973816 11.606413 -10.287585 -4.018404 -7.138945 -10.137027 -9.425965 ... 6.321750 12.820653 17.553917 24.904399 30.941367 37.337512 0.006206 0.091588 1588.161024 1525.601266
101 m00973.wav -8.531343 54.251755 20.963310 13.099079 9.721153 6.015052 10.720785 6.102119 0.694264 ... 7.653338 10.039850 19.457696 25.698457 25.316437 18.259208 0.088824 0.161897 3295.553789 3671.260489
102 m00988.wav -56.091187 36.468166 11.945320 -6.441752 3.778163 5.284157 3.394784 4.265098 7.993917 ... 6.389734 16.538572 24.927608 25.531409 23.668680 16.240189 0.089230 0.167399 3361.504206 3940.349921
103 m00991.wav -406.965424 163.139465 -2.413809 32.456650 7.907843 9.677136 -5.489037 9.523492 -3.102817 ... 8.248197 19.474204 27.322492 31.308818 33.020523 45.577339 0.000300 0.044227 1404.132189 983.761841
104 m00995.wav -223.177246 163.246231 -34.416225 24.187868 -10.304306 3.228735 -12.884593 0.805069 -14.284255 ... 7.791964 16.950850 21.291814 29.557188 35.280038 56.017764 0.000566 0.071648 1371.336609 1253.132224

105 rows × 59 columns

2.3: Toelichting features¶

Voor het maken van het voorspellende model hebben wij feature engineering toegepast op de labeled en unlabeled dataset. De toegevoegde features zijn hierboven te zien. Door het toepassen van feature engineering proberen wij de prestaties van ons voorspellend model te verbeteren. Hieronder zullen wij elke toegevoegde feature toelichten.

Mel-Frequency Cepstral Coefficients (MFCCs)¶

Als eerste voegen wij meerder MFCCs toe aan onze dataframe. Dit zijn coëfficiënten die de vorm van het spectrum van geluiden beschrijven. Ze worden berekend door een Fourier-transformatie toe te passen op kleine tijdvensters van het signaal, waarna de logaritme van de amplitudes wordt genomen, gevolgd door een discrete cosinetransformatie. Het resultaat is een reeks coëfficiënten die de frequentie-inhoud van het geluid op een compacte manier weergeven.

Voor het berekenen van de MFCCs worden de volgende 5 stappen doorlopen:

Stap 1. Framing:¶

$ x_w[n] = x[n] \cdot w[n] $

Bij deze stap wordt het audio-signaal opgedeeld in kleine tijdvensters, ook wel frames genoemd. Dit helpt bij het vastleggen van de tijdsgebonden eigenschappen van het geluid. Het signaal binnen elk frame wordt vermenigvuldigd met een Hamming-vensterfunctie $ w[n] $ om abrupte overgangen aan het begin en einde van het frame te minimaliseren. Het resultaat is het venstergedragde signaal $ x_w[n] $.

Stap 2. Fast Fourier Transform (FFT):¶

$ X(k) = \text{FFT}(x_w[n]) $

Fast Fourier Transform (FFT): Hier wordt de FFT toegepast op elk venstergedragde signaal $ x_w[n] $. Dit leidt tot de decompositie van het signaal van het tijddomein naar het frequentiedomein, waarbij de amplitudes en fasen van de frequentiecomponenten worden verkregen. De resulterende gegevens worden weergegeven als $ X(k) $, waarbij $ k $ de frequentie-index is.

Stap 3. Mel Filterbank:¶

$ S_m = \sum_{k=0}^{N-1} |X(k)|^2 \cdot H_m(k) $

In deze stap wordt de energie in verschillende frequentiebanden gemeten met behulp van een Mel-filterbank. Elk Mel-filter $ H_m(k) $ is ontworpen om de energie in een bepaald frequentiegebied te vertegenwoordigen volgens de Mel-schaal, die de menselijke waarneming van geluidsfrequentie weergeeft. De energie $ S_m $ in elk Mel-filter wordt berekend door de kwadraten van de magnitude van de FFT-resultaten te vermenigvuldigen met de overeenkomstige filterresponsen en deze op te tellen over alle frequentiebanden.

Stap 4. Logarithmic Scaling:¶

$ M_m = \log(S_m) $

Na het verkrijgen van de Mel-energieën, worden deze waarden logaritmisch geschaald. Dit wordt gedaan om de dynamische bereik van de gegevens te comprimeren en om de representatie te verbeteren, aangezien mensen logaritmische schalen beter waarnemen dan lineaire schalen. Het resultaat is een reeks logaritmisch geschaalde Mel-energieën $ M_m $.

Stap 5. Discrete Cosine Transform (DCT):¶

$ C_l = \sum_{m=0}^{M-1} \cos\left(\frac{\pi l (2m + 1)}{2M}\right) \cdot M_m $

Ten slotte worden de logaritmisch geschaalde Mel-energieën onderworpen aan de DCT. Dit proces vertaalt de gegevens van het frequentiedomein naar het cepstrale domein, waarbij de coëfficiënten $ C_l $ worden verkregen. Deze coëfficiënten representeren verschillende frequentiecomponenten van het geluidsspectrum op een compacte en gestructureerde manier.

Gebruikte Symbolen:

  • $ x[n] $: Audio signaal.
  • $ w[n] $: Hamming window.
  • $ X(k) $: Resultaat vab de FFT.
  • $ H_m(k) $: $m$-de Mel filter.
  • $ S_m $: Energie in het $m$-de Mel filter.
  • $ M_m $: Log-Mel energie.
  • $ C_l $: $l$- de MFCC coefficient.
  • $ N $: Aantal frequentiepunten na FFT.
  • $ M $: Aantal Mel filters.
  • $ L $: Aantal gewenste MFCCs.

(Deruty, 2022)

Root Mean Square energy¶

Vervolgens voegen wij Root Mean Square energy (RMS energy) toe. Deze feature geeft aan hoeveel energie het signaal gemiddeld bevat. Bij ons gaat het hier om het geluidssignaal van muzieknummers. Aan de hand van de RMS energy kan de intensiteit van het signaal makkelijk worden beoordeeld. Hoe hoger de RMS energy, hoe luider het signaal gemiddeld is.

Voor het berekenen van de RMS energy nemen wij de RMS van het muzieksignaal. Dit gaat als volgt:

  1. Kwadrateer elk monster $ x[n] $ in het signaal: $ x^2[n] $

  2. Bereken het gemiddelde van deze gekwadrateerde monsters over de hele periode $ N $: $ \text{Gemiddelde} = \frac{1}{N} \sum_{n=0}^{N-1} x^2[n] $

  3. Neem de vierkantswortel van dit gemiddelde: $ \text{RMS} = \sqrt{\text{Gemiddelde}} $

(Hathaway, 2005)

Spectral Contrast¶

We voegen ook spectral contrast toe, een functie die het verschil in amplitude tussen pieken en dalen in een frequentiespectrum meet. Dit biedt inzicht in het perceptuele contrast tussen spectrale pieken en achtergrondgeluid, wat informatie oplevert over de toonkwaliteit van het geluid, in ons geval over verschillende muziekfragmenten.

De waarde van spectral contrast wordt bepaald door eerst het frequentiespectrum van het geluidssignaal op te splitsen in verschillende frequentiebanden. Vervolgens wordt voor elke frequentieband het contrast berekend tussen het gemiddelde energieniveau van die band en het gemiddelde energieniveau van aangrenzende frequentiebanden. Dit contrast wordt genormaliseerd om rekening te houden met verschillen in de absolute energieniveaus van het geluidssignaal. Ten slotte worden de resulterende waarden voor elke frequentieband samengevoegd tot een enkel kenmerk dat de mate van variabiliteit in het geluidsspectrum weergeeft.

Formule spectral contrast:

$$ C_m = \sum_{k=1}^{K} \left| X(k) \right| \cdot \left| X(k) - X_{\text{min}}(k) \right| $$

waarbij:

  • $ C_m $ de spectral contrast feature voor de $ m $-de frequentieband is,
  • $ X(k) $ de amplitude van de $ k $-de frequentie in het spectrum is,
  • $ X_{\text{min}}(k) $ de laagste amplitude in de $ m $-de frequentieband is, en
  • $ K $ het aantal frequentiebanden is.

(Librosa.Feature.Spectral_Contrast — Librosa 0.10.1 Documentation, z.d.)

Tonnetz¶

Tonnetz is een concept uit de muziektheorie dat de harmonische relaties tussen tonen en akkoorden visualiseert door middel van een zeshoekig rooster of diagram, vergelijkbaar met een muzikale kaart waarop de onderlinge verbanden tussen verschillende tonen en akkoorden worden weergegeven.

De waarde van Tonnetz wordt bepaald door de berekening van de afstanden tussen punten op het rooster, wat de mate van harmonische verwantschap aangeeft. Deze berekening maakt gebruik van muziektheoretische concepten, zoals toonafstanden en intervallen, om de harmonische relaties te definiëren.

Punten die dicht bij elkaar liggen op het Tonnetz-rooster vertegenwoordigen sterke harmonische verbindingen, terwijl punten die verder uit elkaar liggen minder nauw verbonden zijn harmonisch gezien. De analyse van Tonnetz biedt inzicht in de harmonische structuur en relaties binnen een muziekstuk, wat van belang kan zijn voor ons voorspellend model.

In ons project maken we gebruik van de librosa-module om de Tonnetz te bepalen. Eerst wordt het audiosignaal omgezet naar een spectrogram, waarna chromagramfuncties worden berekend. De afstanden tussen de chromagrampunten worden vervolgens omgezet naar een zeshoekig Tonnetz-rooster met behulp van een transformatiematrix die de relaties tussen noten weergeeft. Uiteindelijk kan hiermee een visuele weergave worden getoond, waardoor we de muzikale structuur en harmonische samenhang van het signaal kunnen analyseren.

Verder is er geen specifieke formule voor het berekenen van Tonnetz, omdat het een conceptueel model is dat gebaseerd is op muziektheoretische principes en geometrische representatie.

(Librosa.Feature.Tonnetz — Librosa 0.10.1 Documentation, z.d.)

Spectral Rolloff¶

Het spectral rolloff is eenvoudigweg de frequentie die het frequentiegebied onder een bepaald percentage van de totale spectrale energie verdeelt, meestal is dit bij 85%. Bovendien dient het als een indicator van de mate van verandering in spectrale energie naarmate de frequentie toeneemt, waardoor het informatie verschaft over de steilheid van het spectrum van een geluidssignaal.

De formule is gegeven door:

$ R = \frac{\sum_{f} S(f)}{\sum_{f} S(f_{\text{totaal}})} \cdot 100 \leq p $

  • $ R $: Spectraalverloop frequentie.
  • $ S(f) $: Spectrale energie bij frequentie $ f $.
  • $ S(f_{\text{totaal}}) $: Totale spectrale energie.
  • $ p $: Percentage van de totale spectrale energie (bijvoorbeeld 85% of 95%).

(Librosa.Feature.Spectral_Rolloff — Librosa 0.10.1 Documentation, z.d.)

Chroma Features¶

Chroma-kenmerken bieden inzicht in de toonhoogteverdeling in muziek, waarbij de tonale kwaliteiten worden benadrukt. Het chromagram, een grafische weergave van de chroma-kenmerken, deelt het muzikale octaaf op in 12 gelijke delen, die overeenkomen met de 12 muzikale toonhoogteklassen: {C, C♯, D, D♯, E, F, F♯, G, G♯, A, A♯, B}

De chroma-Features worden berekend met behulp van het chromagram, waarbij elke chromawaarde (C_i) wordt bepaald door de som van de magnitudes van de frequentiecomponenten die overeenkomen met de betreffende toonhoogteklasse. Met andere woorden, voor elke toonhoogteklasse wordt de magnitude van alle frequentiecomponenten die in die klasse vallen opgeteld.(Wikipedia contributors, 2024)

De formule om de chromawaarde $ C_i $ te berekenen is:

$ C_i = \sum_{j} \text{magnitude}(f_j) $

Hierbij staat $ C_i $ voor de chromawaarde voor de $ i $-de toonhoogteklasse, en magnitude$ \text{magnitude}(f_j) $ staat voor de magnitude van de $ j $-de frequentiecomponent in het spectrum die overeenkomt met de betreffende toonhoogteklasse. De sommatie wordt uitgevoerd over alle frequentiecomponenten die overeenkomen met de $ i $-de toonhoogteklasse.

Dit proces wordt herhaald voor elk van de twaalf toonhoogteklassen om de complete chroma-kenmerken te verkrijgen.

Mel Spectral Contrast Features¶

We voegen ook Mel Spectral Contrast features toe, een functie die het verschil in amplitude tussen pieken en dalen in een frequentiespectrum meet, maar dan op basis van de Mel-schaal. Dit biedt inzicht in het perceptuele contrast tussen spectrale pieken en achtergrondgeluid, waardoor we informatie verkrijgen over de toonkwaliteit van het geluid, met name voor verschillende muziekfragmenten.

De waarde van Mel Spectral Contrast wordt bepaald door eerst het frequentiespectrum van het geluidssignaal om te zetten naar de Mel-schaal. Vervolgens wordt voor elke Mel-frequentieband het contrast berekend tussen het gemiddelde energieniveau van die band $ \mu_m $ en het gemiddelde energieniveau van aangrenzende Mel-frequentiebanden. Dit contrast wordt gewogen met het kwadraat van de afstand tot het gemiddelde $ (m - \mu_m)^2 $ en genormaliseerd om rekening te houden met verschillen in de absolute energieniveaus van het geluidssignaal. Ten slotte worden de resulterende waarden voor elke Mel-frequentieband samengevoegd tot een enkel kenmerk dat de mate van variabiliteit in het geluidsspectrum weergeeft. Dit kenmerk voegen wij uiteindelijk toe aan het dataframe.(Verma, 2021)

Formule voor mel spectral contrast:

$$ text{Mel_Spectral_Contrast}(X) = \frac{\sum_{m=1}^{M} (m - \mu_m)^2 \cdot S_m}{\sum_{m=1}^{M} S_m} $$

Spectral Flatness¶

Spectral Flatness is een audiokenmerk dat de mate van variabiliteit in het frequentiespectrum van een geluidssignaal beschrijft. Het geeft aan hoe gelijkmatig de energie verdeeld is over de frequentiebanden in het spectrum. Een hoge waarde voor Spectral Flatness suggereert een gelijkmatige verdeling van energie over het spectrum, wat kan duiden op een geluidssignaal met een breed frequentiebereik en een gebrek aan dominante frequentiepieken. Aan de andere kant duidt een lage waarde op een sterk piekachtig spectrum, waarbij de energie geconcentreerd is rond specifieke frequenties.

De waarde van Spectral Flatness wordt berekend door de meetkundige gemiddelde (GM) te delen door het rekenkundige gemiddelde (AM) van de amplitudes van de frequentiecomponenten in het spectrum. Met andere woorden, het wordt berekend als de verhouding tussen het gemiddelde van de logaritmische amplitudes en de logaritmische amplitude van het gemiddelde. Deze verhouding wordt vaak genormaliseerd om waarden tussen 0 en 1 te krijgen, waarbij een waarde dichter bij 1 wijst op een vlakker spectrum en een waarde dichter bij 0 op een meer piekachtig spectrum.(Wikipedia contributors, 2023)

In formulevorm kan Spectral Flatness worden uitgedrukt als: $$ \text{Spectral Flatness} = \frac{{\exp\left(\frac{1}{N} \sum_{i=1}^{N} \ln(A_i)\right)}}{{\frac{1}{N} \sum_{i=1}^{N} A_i}} $$

Waarbij $ (A_i) $ de amplitude van de $i$-de frequentiecomponent is en $N$ het totale aantal frequentiecomponenten in het spectrum.

Zero Crossing Rate¶

Zero Crossing Rate (ZCR) is een audiokenmerk dat de frequentie van signaalovergangen door nul in een geluidssignaal meet. Het houdt rekening met het aantal keren dat het signaal de horizontale as kruist en geeft daarmee een indicatie van de veranderingen in het signaal over de tijd. Een hogere ZCR duidt op meer veranderingen in het signaal en kan worden geassocieerd met geluiden met een hogere mate van dynamiek of ruwheid, wat waardevolle informatie biedt over de aard en complexiteit van het geluidssignaal.

De Zero Crossing Rate wordt berekend met behulp van de formule:

$$ \text{ZCR} = \frac{1}{N-1} \sum_{n=1}^{N-1} | \text{sign}(x[n]) - \text{sign}(x[n-1]) | $$

Hierbij staat $ N $ voor het aantal samples in het signaal en $ x[n] $ voor de waarde van het signaal op het tijdstip $ n $. De ZCR geeft het gemiddelde aantal keren aan waarop het signaal de waarde nul kruist per tijdsinterval, wat een indicatie kan zijn van de mate van verandering of activiteit in het signaal.(Torres-García et al., 2022)

Bandwidth¶

Bandbreedte, ook wel bekend als Bandwidth, refereert naar het bereik van frequenties binnen een signaal of een frequentiespectrum. Het indiceert hoeveel ruimte een signaal beslaat op de frequentieschaal en wordt uitgedrukt in hertz (Hz). Een brede bandbreedte suggereert dat het signaal een uitgebreid frequentiebereik omvat, terwijl een smalle bandbreedte aanduidt dat het signaal beperkt is tot een klein frequentiebereik.

De waarde van de bandbreedte wordt vastgesteld door het verschil te berekenen tussen de hoogste en laagste frequentiecomponenten in het signaal of frequentiespectrum. Dit kan worden uitgedrukt door de frequentie van het hoogste punt in het spectrum te verminderen met de frequentie van het laagste punt. In een frequentiespectrum wordt de bandbreedte beïnvloed door de breedte van de pieken die de energie van het signaal op verschillende frequenties weergeven. Een bredere piek duidt op een grotere bandbreedte, terwijl een smallere piek wijst op een kleinere bandbreedte.

De formule voor het berekenen van de bandbreedte $ B $ is:

$$ B = f_{\text{max}} - f_{\text{min}} $$

Waarbij $ f_{\text{max}} $ de frequentie van het hoogste punt in het spectrum vertegenwoordigt, en $ f_{\text{min}} $ de frequentie van het laagste punt in het spectrum.

Centroids¶

Centroids, in de context van signaalverwerking en akoestische analyse, zijn maatstaven die worden gebruikt om de centrale frequentie van een signaal of een frequentiespectrum te kwantificeren. Het concept van een centroid komt voort uit de statistiek, waar het verwijst naar het massamiddelpunt van een geometrische vorm.

In de context van akoestische analyse, vertegenwoordigt de centroid de gemiddelde frequentie van een geluidssignaal. Het is een belangrijk kenmerk dat informatie geeft over waar de meeste energie in het frequentiespectrum van het signaal geconcentreerd is. De centroid kan bijvoorbeeld aangeven of een geluidssignaal eerder laagfrequent (lage centroid) of hoogfrequent (hoge centroid) is.(Wikipedia contributors, 2024a)

Hieronder staat de formule voor het berekenen van de centroid.

De formule voor het berekenen van de centroid ($ C $) is:

$$ C = \frac{\sum_{i} f_i \cdot A_i}{\sum_{i} A_i} $$
  • $ C $: Centroid (gemiddelde frequentie).
  • $ f_i $: Frequentie van de $ i $-de component.
  • $ A_i $: Amplitude (of magnitude) van de $ i $-de component.

2.4: Dataframe scalen¶

In [11]:
def scaler(df, labeled=True):
    """
    Normalizes numeric columns of a DataFrame using sklearn MinMaxScaler

    Parameters:
    - df (pd.DataFrame): Input DataFrame containing numeric columns to be standardized.

    Returns:
    - pd.DataFrame: A new DataFrame with standardized numeric columns.
    """
    scaler = MinMaxScaler()
    if labeled:
        cols = ['filename', 'genre']
    else:
        cols = ['filename']

    scaled_df = df[df.columns.difference(cols)]
    scaled_df = pd.DataFrame(scaler.fit_transform(scaled_df))
    scaled_df.columns = df[df.columns.difference(cols)].columns
    scaled_df = pd.concat([pd.DataFrame(df[cols]), scaled_df], axis=1)
        
    return scaled_df
In [12]:
df = scaler(unlabeled_df, labeled=False)
display(df.head())
filename bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 m00003.wav 0.448700 0.463313 0.536488 0.723331 0.959317 0.760275 0.628028 0.688307 0.598168 ... 0.833694 0.075079 0.446538 0.601636 0.327193 0.433945 0.462291 0.286771 0.359017 0.485893
1 m00012.wav 0.531218 0.660143 0.431423 0.385958 0.370729 0.728339 0.920078 0.984592 0.734350 ... 0.892147 0.220823 0.583361 0.565916 0.462638 0.419674 0.263680 0.141557 0.452145 0.803681
2 m00013.wav 0.370122 0.372563 0.584292 0.383882 0.094401 0.600906 0.101713 0.107339 0.045195 ... 0.666645 0.017305 0.366321 0.539972 0.673517 0.074136 0.513645 0.213762 0.537025 0.504171
3 m00043.wav 0.449617 0.502558 0.360861 0.606008 0.721541 0.650086 0.515102 0.610834 0.835636 ... 0.809051 0.073118 0.473765 0.552052 0.282639 0.611838 0.573084 0.650816 0.246407 0.518485
4 m00044.wav 0.000000 0.022579 0.115212 0.573531 0.077500 0.000000 0.021968 0.402056 0.456909 ... 0.110577 0.000000 0.016254 0.234122 0.581224 0.558948 0.000000 0.204898 0.412068 0.112807

5 rows × 59 columns

In [13]:
df.drop("filename", axis=1, inplace=True)
df
Out[13]:
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 0.448700 0.463313 0.536488 0.723331 0.959317 0.760275 0.628028 0.688307 0.598168 0.561027 ... 0.833694 0.075079 0.446538 0.601636 0.327193 0.433945 0.462291 0.286771 0.359017 0.485893
1 0.531218 0.660143 0.431423 0.385958 0.370729 0.728339 0.920078 0.984592 0.734350 0.421036 ... 0.892147 0.220823 0.583361 0.565916 0.462638 0.419674 0.263680 0.141557 0.452145 0.803681
2 0.370122 0.372563 0.584292 0.383882 0.094401 0.600906 0.101713 0.107339 0.045195 0.394422 ... 0.666645 0.017305 0.366321 0.539972 0.673517 0.074136 0.513645 0.213762 0.537025 0.504171
3 0.449617 0.502558 0.360861 0.606008 0.721541 0.650086 0.515102 0.610834 0.835636 1.000000 ... 0.809051 0.073118 0.473765 0.552052 0.282639 0.611838 0.573084 0.650816 0.246407 0.518485
4 0.000000 0.022579 0.115212 0.573531 0.077500 0.000000 0.021968 0.402056 0.456909 0.020207 ... 0.110577 0.000000 0.016254 0.234122 0.581224 0.558948 0.000000 0.204898 0.412068 0.112807
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
100 0.263711 0.243890 0.265539 0.359021 0.159503 0.468268 0.266454 0.704754 0.361255 0.358404 ... 0.101925 0.050949 0.226399 0.622226 0.693546 0.459072 0.276723 0.567309 0.326597 0.329244
101 0.917984 0.889847 0.497698 0.499467 0.558340 0.483318 0.761525 0.645451 0.501318 0.455329 ... 0.046845 0.746303 0.913696 0.645293 0.282011 0.376291 0.050440 0.469340 0.408466 0.714738
102 0.943256 0.970857 0.667814 0.794760 0.763969 0.727771 0.644380 0.533659 0.477373 0.326759 ... 0.023429 0.749720 0.964501 0.373759 0.315903 0.546494 0.660520 0.155468 0.169731 0.744902
103 0.193191 0.080768 0.160092 0.553143 0.150116 0.016812 0.003486 0.402058 0.896679 0.238091 ... 0.411653 0.001241 0.108823 0.000000 0.587919 0.992714 0.133532 0.632113 0.972459 0.069575
104 0.180624 0.161863 0.248791 0.448866 0.112806 0.249699 0.316415 0.632128 0.315825 0.031996 ... 0.609460 0.003478 0.167655 0.498418 0.556749 0.620428 0.040981 0.535506 0.335021 0.219916

105 rows × 58 columns

Hoofdstuk 3: Unsupervised Learning¶

3.1 Clustering¶

In [14]:
# overgenomen uit ml les
inertia = []
k = range(1, 11)

for i in k:
    model = KMeans(n_clusters=i, n_init=10)

    model.fit(df)

    inertia.append(model.inertia_)

plt.plot(k, inertia, '-x')
plt.xlabel('Aantal (k) clusters')
plt.ylabel('inertia')
plt.xticks(k)
plt.show()
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(

Nu voeren we Kmeans clustering uit om zo een visualisatie van de inertia te krijgen. Inertia is een manier om te achterhalen hoeveel clusters we moeten gebruiken, door het te visualiseren kunnen we gemakkelijk zien wat de beste waarde is. hier kunnen we zien dat het elleboog op het punt k = 3 staat. dit betekent dat de beste aantal clusters 3 is. de reden dat het elleboog punt het beste kan worden gebruikt is omdat hier het balans ligt tussen minimale afstanden van punten tot het centrum van hun cluster en overfitting. als het gekozen punt verhoogt wordt betekent dit dus dat de prestaties van het model op de training set misschien verhoogt worden maar niet op de test set het verlagen is natuurlijk ook niet aan te raden sinds zelfs het verlagen van 3 naar 2 al een grootte impact heeft op de waarde van de inertia en dus spreiding van de punten tot hun cluster centrum.

In [15]:
data = df
data
Out[15]:
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 0.448700 0.463313 0.536488 0.723331 0.959317 0.760275 0.628028 0.688307 0.598168 0.561027 ... 0.833694 0.075079 0.446538 0.601636 0.327193 0.433945 0.462291 0.286771 0.359017 0.485893
1 0.531218 0.660143 0.431423 0.385958 0.370729 0.728339 0.920078 0.984592 0.734350 0.421036 ... 0.892147 0.220823 0.583361 0.565916 0.462638 0.419674 0.263680 0.141557 0.452145 0.803681
2 0.370122 0.372563 0.584292 0.383882 0.094401 0.600906 0.101713 0.107339 0.045195 0.394422 ... 0.666645 0.017305 0.366321 0.539972 0.673517 0.074136 0.513645 0.213762 0.537025 0.504171
3 0.449617 0.502558 0.360861 0.606008 0.721541 0.650086 0.515102 0.610834 0.835636 1.000000 ... 0.809051 0.073118 0.473765 0.552052 0.282639 0.611838 0.573084 0.650816 0.246407 0.518485
4 0.000000 0.022579 0.115212 0.573531 0.077500 0.000000 0.021968 0.402056 0.456909 0.020207 ... 0.110577 0.000000 0.016254 0.234122 0.581224 0.558948 0.000000 0.204898 0.412068 0.112807
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
100 0.263711 0.243890 0.265539 0.359021 0.159503 0.468268 0.266454 0.704754 0.361255 0.358404 ... 0.101925 0.050949 0.226399 0.622226 0.693546 0.459072 0.276723 0.567309 0.326597 0.329244
101 0.917984 0.889847 0.497698 0.499467 0.558340 0.483318 0.761525 0.645451 0.501318 0.455329 ... 0.046845 0.746303 0.913696 0.645293 0.282011 0.376291 0.050440 0.469340 0.408466 0.714738
102 0.943256 0.970857 0.667814 0.794760 0.763969 0.727771 0.644380 0.533659 0.477373 0.326759 ... 0.023429 0.749720 0.964501 0.373759 0.315903 0.546494 0.660520 0.155468 0.169731 0.744902
103 0.193191 0.080768 0.160092 0.553143 0.150116 0.016812 0.003486 0.402058 0.896679 0.238091 ... 0.411653 0.001241 0.108823 0.000000 0.587919 0.992714 0.133532 0.632113 0.972459 0.069575
104 0.180624 0.161863 0.248791 0.448866 0.112806 0.249699 0.316415 0.632128 0.315825 0.031996 ... 0.609460 0.003478 0.167655 0.498418 0.556749 0.620428 0.040981 0.535506 0.335021 0.219916

105 rows × 58 columns

Fantasy Cup Spelers Dataset¶

Player Speed Defence Pas Shot Creativeness
Vikram 90 70 80 75 85
Bob 60 90 50 40 50
Mark 80 65 90 70 80
Sami 95 40 60 85 70
Daan - 85 - - -
Penaldo 75 30 60 95 40
Pessi 70 50 85 60 90

Initialization:¶

Kies het aantal clusters (K) dat je wilt creëren in je dataset. Initialiseerd de centroids willekeurig. Deze centroids vertegenwoordigen de initiële centroid van de clusters. Toewijzingsstap:

Voor elk datapunt in de dataset, bereken de afstand tot elk centroid. Wijs het datapunt toe aan de cluster waarvan de centroid het dichtstbij is met behulp van de Euclidische afstand.

Herbereken de centroids van elke cluster door het gemiddelde te nemen van alle datapunten die aan die cluster zijn toegewezen.

Herhaal de toewijzings- en updatestappen totdat convergentie is bereikt. Convergentie treedt op wanneer de centroids niet significant meer veranderen of wanneer een bepaald aantal iteraties is bereikt. Output:

Het algoritme geeft K clusters uit, elk gerepresenteerd door zijn centroid.

Opmerking: Ondanks dat Daan NaN-waarden heeft, zal hij toch worden toegewezen aan een centrum op basis van bestaande kenmerken.

In [16]:
class Clustering:
    def __init__(self, data):
        """
        Initializes the Clustering object with input data.

        Parameters:
        - data (numpy.ndarray): Input data for clustering.
        """
        self.data = data
        self.cluster_labels = None



        def calculate_silhouette_score(self):
            if self.cluster_labels is not None:
                if len(np.unique(self.cluster_labels)) > 1:
                    self.silhouette_score = silhouette_score(self.data, self.cluster_labels)
                else:
                    print("Silhouette score cannot be calculated with only one cluster.")
            else:
                print("Cluster labels not available. Run a clustering algorithm first.")

    def cluster_kmeans(self, n_clusters=3):
        """
        Performs KMeans clustering on the input data.

        Parameters:
        - n_clusters (int): Number of clusters.

        Returns:
        - numpy.ndarray: An array containing the input data and assigned cluster labels.
        """
        kmeans = KMeans(n_clusters=n_clusters, random_state=42)
        self.cluster_labels = kmeans.fit_predict(self.data)
        return np.column_stack((self.data, self.cluster_labels))

    def cluster_agglomerative(self, n_clusters=3):
        """
        Performs Agglomerative clustering on the input data.

        Parameters:
        - n_clusters (int): Number of clusters.

        Returns:
        - numpy.ndarray: An array containing the input data and assigned cluster labels.
        """
        agglomerative = AgglomerativeClustering(n_clusters=n_clusters)
        self.cluster_labels = agglomerative.fit_predict(self.data)
        return np.column_stack((self.data, self.cluster_labels))

    def cluster_dbscan(self, eps=0.5, min_samples=5):
        """
        Performs DBSCAN clustering on the input data.

        Parameters:
        - eps (float): The maximum distance between two samples for one to be considered as in the neighborhood of the other.
        - min_samples (int): The number of samples (or total weight) in a neighborhood for a point to be considered as a core point.

        Returns:
        - numpy.ndarray: An array containing the input data and assigned cluster labels.
        """
        dbscan = DBSCAN(eps=eps, min_samples=min_samples)
        self.cluster_labels = dbscan.fit_predict(self.data)
        return np.column_stack((self.data, self.cluster_labels))

    def cluster_birch(self, n_clusters=3):
        """
        Performs Birch clustering on the input data.

        Parameters:
        - n_clusters (int): Number of clusters.

        Returns:
        - numpy.ndarray: An array containing the input data and assigned cluster labels.
        """
        birch = Birch(n_clusters=n_clusters)
        self.cluster_labels = birch.fit_predict(self.data)
        return np.column_stack((self.data, self.cluster_labels))

    def cluster_meanshift(self, bandwidth=0.5):
        """
        Performs MeanShift clustering on the input data.

        Parameters:
        - bandwidth (float): Bandwidth parameter for MeanShift.

        Returns:
        - numpy.ndarray: An array containing the input data and assigned cluster labels.
        """
        meanshift = MeanShift(bandwidth=bandwidth)
        self.cluster_labels = meanshift.fit_predict(self.data)
        return np.column_stack((self.data, self.cluster_labels))
In [17]:
unlabeled_data = data
unlabeled_data
Out[17]:
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 0.448700 0.463313 0.536488 0.723331 0.959317 0.760275 0.628028 0.688307 0.598168 0.561027 ... 0.833694 0.075079 0.446538 0.601636 0.327193 0.433945 0.462291 0.286771 0.359017 0.485893
1 0.531218 0.660143 0.431423 0.385958 0.370729 0.728339 0.920078 0.984592 0.734350 0.421036 ... 0.892147 0.220823 0.583361 0.565916 0.462638 0.419674 0.263680 0.141557 0.452145 0.803681
2 0.370122 0.372563 0.584292 0.383882 0.094401 0.600906 0.101713 0.107339 0.045195 0.394422 ... 0.666645 0.017305 0.366321 0.539972 0.673517 0.074136 0.513645 0.213762 0.537025 0.504171
3 0.449617 0.502558 0.360861 0.606008 0.721541 0.650086 0.515102 0.610834 0.835636 1.000000 ... 0.809051 0.073118 0.473765 0.552052 0.282639 0.611838 0.573084 0.650816 0.246407 0.518485
4 0.000000 0.022579 0.115212 0.573531 0.077500 0.000000 0.021968 0.402056 0.456909 0.020207 ... 0.110577 0.000000 0.016254 0.234122 0.581224 0.558948 0.000000 0.204898 0.412068 0.112807
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
100 0.263711 0.243890 0.265539 0.359021 0.159503 0.468268 0.266454 0.704754 0.361255 0.358404 ... 0.101925 0.050949 0.226399 0.622226 0.693546 0.459072 0.276723 0.567309 0.326597 0.329244
101 0.917984 0.889847 0.497698 0.499467 0.558340 0.483318 0.761525 0.645451 0.501318 0.455329 ... 0.046845 0.746303 0.913696 0.645293 0.282011 0.376291 0.050440 0.469340 0.408466 0.714738
102 0.943256 0.970857 0.667814 0.794760 0.763969 0.727771 0.644380 0.533659 0.477373 0.326759 ... 0.023429 0.749720 0.964501 0.373759 0.315903 0.546494 0.660520 0.155468 0.169731 0.744902
103 0.193191 0.080768 0.160092 0.553143 0.150116 0.016812 0.003486 0.402058 0.896679 0.238091 ... 0.411653 0.001241 0.108823 0.000000 0.587919 0.992714 0.133532 0.632113 0.972459 0.069575
104 0.180624 0.161863 0.248791 0.448866 0.112806 0.249699 0.316415 0.632128 0.315825 0.031996 ... 0.609460 0.003478 0.167655 0.498418 0.556749 0.620428 0.040981 0.535506 0.335021 0.219916

105 rows × 58 columns

In [18]:
k = 3  

kmeans = KMeans(n_clusters=k, n_init=10, random_state=42)

unlabeled_data['cluster'] = kmeans.fit_predict(unlabeled_data)

print(unlabeled_data['cluster'].value_counts())
C:\Users\jesse\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
  warnings.warn(
cluster
1    40
2    39
0    26
Name: count, dtype: int64

hier voeren we Kmeans op de unlabeled dataset en voegen de clusters toe als kolommen.

In [19]:
unlabeled_cluster = unlabeled_data.groupby('cluster').agg('mean')

display(unlabeled_cluster)
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
cluster
0 0.523447 0.573262 0.556266 0.645412 0.599113 0.648855 0.666862 0.673735 0.685147 0.605894 ... 0.883251 0.153669 0.552364 0.542640 0.341978 0.451951 0.441608 0.395708 0.413671 0.620927
1 0.234479 0.197888 0.223155 0.361775 0.196132 0.386166 0.210448 0.400807 0.288687 0.200952 ... 0.337053 0.015374 0.197929 0.528294 0.626156 0.484505 0.415608 0.492764 0.430684 0.260223
2 0.826579 0.747882 0.424069 0.544913 0.483928 0.602510 0.586091 0.560396 0.512694 0.404926 ... 0.067970 0.551744 0.782709 0.530890 0.376735 0.436866 0.419808 0.457613 0.418868 0.610555

3 rows × 58 columns

In [20]:
labeled = labeled_df
display(labeled)
filename genre mfcc1_mean mfcc2_mean mfcc3_mean mfcc4_mean mfcc5_mean mfcc6_mean mfcc7_mean mfcc8_mean ... mel_spectral_contrast2_mean mel_spectral_contrast3_mean mel_spectral_contrast4_mean mel_spectral_contrast5_mean mel_spectral_contrast6_mean mel_spectral_contrast7_mean spectral_flatness zcr bandwith centroid
8 m00002.wav jazz -298.807953 112.078209 6.485770 28.386517 -6.764679 16.651894 -11.809684 12.746783 ... 6.739004 17.720329 31.856210 37.582171 36.234603 42.188262 0.001252 0.051222 1919.917650 1451.498371
9 m00039.wav reggae -169.243668 110.447716 -8.553957 43.898693 0.266454 26.646509 -14.365674 13.026835 ... 8.001072 15.869485 22.225069 29.004593 27.931632 43.075836 0.004306 0.072178 2019.252686 1811.358216
43 m00041.wav pop -18.854591 71.328522 -3.743232 -1.396592 0.710347 -1.049137 -1.052407 -0.249471 ... 5.718698 13.057337 17.657730 23.827369 27.071292 18.570913 0.078506 0.152910 2992.192112 3111.061099
29 m00072.wav disco -69.599335 83.059570 -16.599524 0.119469 7.415704 0.769619 1.337008 4.690686 ... 6.370224 12.197484 18.810620 25.720595 24.686535 23.377385 0.038270 0.120259 2709.990169 2625.095044
34 m00096.wav disco -91.886307 87.604057 -2.058175 34.285538 -18.153370 19.344702 -14.697328 17.116173 ... 4.989545 15.582052 20.384724 25.294628 29.771765 46.579813 0.009796 0.115890 2486.020650 2550.135384
48 m00102.wav classical -436.842285 154.113205 -19.859337 20.183603 3.164744 0.446392 -6.530639 -4.603339 ... 8.704671 18.088766 26.164080 36.452103 37.462534 35.556624 0.000976 0.066325 1517.856160 1173.931066
25 m00112.wav reggae -265.694977 80.615921 9.722022 36.657085 27.836796 12.086828 15.014478 15.116937 ... 3.927251 15.154408 20.473471 26.257647 25.445807 24.486615 0.026549 0.060883 2595.217468 2113.391953
4 m00138.wav reggae -198.632797 102.413582 -10.031449 30.802383 -3.310606 20.276924 -5.637373 28.095631 ... 8.111595 11.985461 21.690643 26.647311 26.212014 47.129611 0.005168 0.070323 2295.466240 1995.600514
5 m00192.wav classical -85.264992 118.423058 -31.472771 20.282015 -4.417709 6.922598 -10.950942 -1.620085 ... 5.707315 9.457773 16.339001 23.798978 26.500622 37.537164 0.010484 0.111613 1965.028715 1878.774030
22 m00206.wav hiphop -92.591652 77.412682 -32.756371 54.596119 -13.199195 33.656281 -12.085772 28.015388 ... 3.856905 11.629413 12.605242 16.697020 17.101189 52.441041 0.018014 0.157603 2295.456105 2735.795802
1 m00230.wav country -89.736382 36.286205 11.469535 37.495590 9.199136 3.740519 2.840358 6.811540 ... 4.663522 12.311560 17.618104 28.147029 26.801658 22.182678 0.093471 0.212375 3053.066772 3760.424488
47 m00236.wav classical -368.465942 124.265991 -31.582445 17.601763 2.322930 -5.949597 -11.807772 -5.547481 ... 4.495887 16.020693 30.754881 34.882406 36.114791 54.128590 0.000620 0.078817 1415.453381 1329.264956
0 m00248.wav metal -75.517509 81.911423 -22.081079 69.876999 -11.740438 25.740246 -18.518965 27.027710 ... 2.714730 9.285961 13.318006 15.142012 17.020385 57.419416 0.018547 0.158776 2337.227557 2656.165734
45 m00253.wav blues -3.559072 92.927788 -25.118544 45.187210 -10.871894 31.862423 -18.057922 28.628012 ... 3.783532 12.969313 16.648841 21.717000 24.115583 57.660211 0.010716 0.124735 2358.384492 2443.238916
40 m00298.wav blues -213.204529 115.154541 -11.720503 39.032555 -20.357819 13.089926 -9.180815 9.023639 ... 5.069766 14.647256 24.722086 30.720819 27.451555 51.615333 0.002710 0.079215 1973.690309 1817.529547
27 m00313.wav blues -249.289398 130.987030 0.677390 66.685783 17.897806 4.623737 6.598308 1.249050 ... 4.712652 16.387888 25.459972 31.543672 29.853833 51.528179 0.000401 0.051725 1463.539070 1109.721379
39 m00338.wav blues -55.596630 114.951218 -37.069351 64.904381 -7.164943 15.254702 -16.235331 18.643576 ... 3.855680 11.104764 19.421730 24.030727 23.153801 59.228233 0.004599 0.106627 1927.439014 1977.108904
44 m00339.wav rock -70.117538 94.181656 -50.328033 46.962898 -16.114254 19.996185 -23.959724 16.344028 ... 3.767756 8.777708 17.115519 22.762585 21.009458 66.166621 0.014635 0.150945 2102.197363 2471.302264
32 m00351.wav jazz -192.517395 113.183693 -6.373058 39.593094 -9.189579 17.204056 -19.114599 12.203582 ... 4.895479 13.100308 18.168636 31.750680 30.777813 52.074700 0.003388 0.076921 2107.923976 1825.975175
16 m00400.wav blues -350.358887 169.546326 31.780231 16.713755 28.661758 19.249090 7.846700 10.039796 ... 6.831016 15.706191 25.741073 31.403125 30.358803 22.970648 0.000457 0.021701 995.093473 570.150688
13 m00421.wav pop -77.941887 32.364956 20.077257 27.633484 16.262774 5.319164 13.481539 3.503227 ... 9.366871 17.067590 22.242897 24.953492 25.764494 21.049781 0.066714 0.144787 3223.867461 3727.248835
6 m00429.wav hiphop -109.509178 97.389557 -20.619621 37.366669 -3.125998 28.611204 -14.169424 23.862110 ... 4.376860 8.659314 15.042835 21.151530 22.051505 50.071179 0.011507 0.104502 2327.237804 2272.834439
46 m00435.wav classical -207.377472 117.607780 -41.404255 13.253087 -1.688240 15.532319 -10.362769 -1.863690 ... 4.804461 20.265242 22.574447 30.592474 40.035231 56.377036 0.000801 0.108258 1551.481235 1623.829998
35 m00454.wav hiphop -103.138268 74.090218 -22.434641 51.604790 -7.079813 32.670975 -14.993524 19.642502 ... 5.691232 11.065545 15.903900 23.839271 22.870837 53.006697 0.016491 0.117624 2396.648334 2670.154564
49 m00477.wav classical -412.959259 138.519836 -19.262381 34.146679 8.975063 -4.294471 4.152584 0.881950 ... 5.582785 20.293131 30.609275 42.342264 38.614245 38.829502 0.000672 0.060885 1467.205561 1171.844481
14 m00501.wav jazz -168.123230 108.271530 -14.149940 30.457626 3.894506 4.676564 3.187918 1.927635 ... 5.479137 19.986478 27.292205 31.991843 30.909894 30.390149 0.006512 0.067522 2091.113741 1725.634530
12 m00503.wav metal -93.591331 89.894020 -55.902695 51.637325 -5.558396 28.777815 -13.892873 27.350773 ... 6.208451 8.601995 15.799067 22.311950 21.688064 62.071454 0.012679 0.151809 2037.300179 2463.449458
42 m00513.wav pop -52.649151 47.782742 13.455156 2.213427 13.251046 9.133875 14.447654 0.873837 ... 7.743124 10.976887 14.634204 21.740248 26.191926 20.857168 0.097539 0.210715 3426.219520 4052.888670
36 m00553.wav disco -95.103706 109.353172 -30.161179 16.358656 20.039730 11.962816 3.485530 6.173969 ... 4.868121 11.508925 16.536470 23.614719 23.721108 29.510298 0.026017 0.103663 2143.899287 2117.550762
33 m00606.wav country -171.339005 137.354568 7.964579 28.539564 2.708197 -0.501510 -2.956100 -1.159342 ... 6.591413 10.533838 16.756754 25.936614 26.975167 28.856986 0.007312 0.063856 1959.987665 1469.784950
7 m00623.wav reggae -94.140526 68.229942 3.975480 7.631387 12.791034 -3.185735 -3.907444 3.817600 ... 7.047003 16.691960 18.115008 23.019129 24.171044 21.322361 0.065537 0.136075 2974.065401 3086.186001
3 m00627.wav metal -57.683388 101.432327 -41.485245 55.130600 -23.349279 28.151102 -12.139105 18.150204 ... 4.042038 9.291089 13.214748 17.212025 17.637471 58.275039 0.012321 0.141317 2092.439557 2378.392048
17 m00629.wav country -82.962616 122.532806 -16.658556 40.015911 -15.007229 21.670378 -18.256283 18.361864 ... 5.854305 15.034976 18.742446 24.931771 25.955064 54.375683 0.005227 0.081104 2116.237657 1881.161818
28 m00633.wav country -122.427414 111.483139 -13.694294 53.828403 -2.677765 19.677549 -14.138923 13.743676 ... 5.908667 12.477974 21.097610 27.547413 27.157049 52.619927 0.005293 0.078317 2085.606346 1879.937791
2 m00637.wav hiphop -122.780525 95.061287 -29.363251 46.780045 -15.998561 27.117586 -13.113779 20.258003 ... 6.453961 10.992537 16.350120 21.788517 20.466351 50.293757 0.013041 0.113229 2181.589027 2290.107318
38 m00658.wav hiphop -105.569511 76.674911 -24.774305 55.198990 -8.257657 28.899265 -15.747581 26.304575 ... 3.862585 12.135674 13.885980 19.403479 20.607843 53.621895 0.013526 0.138555 2350.956242 2628.976734
21 m00671.wav reggae -251.392563 105.855774 2.220024 32.484390 16.563274 8.290815 10.365557 5.504937 ... 6.202627 13.453879 14.213644 19.654592 19.496502 29.640906 0.026613 0.089780 2298.145722 2004.353847
23 m00676.wav pop 9.675074 88.826073 -20.293680 5.101305 -2.917802 -0.188181 0.327482 1.604321 ... 3.538988 9.555049 13.832719 17.227212 19.715628 21.549278 0.064364 0.131656 2559.529362 2569.255823
10 m00677.wav country -139.623062 128.793533 -20.249359 49.443615 -2.160360 16.988024 -8.355296 9.818295 ... 7.767790 18.787435 27.903697 35.160599 34.218542 48.088884 0.001416 0.061745 1690.685707 1425.263509
20 m00678.wav metal -95.339104 92.066360 -28.061176 52.796284 -3.018975 25.395771 -6.135991 20.098093 ... 3.902639 12.254983 15.608343 18.157071 18.915123 47.654008 0.011364 0.115765 2115.725580 2241.454932
31 m00716.wav jazz -202.608383 105.845734 -20.537838 43.897964 -14.272349 25.299541 -15.592009 15.311663 ... 5.576754 14.930754 16.889706 26.349590 28.195721 51.507869 0.006012 0.093247 2084.706315 2034.579158
18 m00762.wav disco -155.637314 60.568497 23.166864 31.186348 25.054384 16.906994 9.438360 8.449133 ... 4.454963 14.698277 18.425711 22.050855 20.513930 20.602898 0.045092 0.101249 2990.754025 2909.983968
37 m00772.wav rock -125.065109 115.203308 -48.004681 52.843102 -13.491938 21.765408 -12.428561 20.139965 ... 5.201100 10.631122 18.387787 22.359313 22.331249 61.219149 0.007513 0.121824 1926.895810 2077.166788
26 m00773.wav pop -204.752975 101.605118 26.384224 9.185719 10.460928 5.108919 -9.447198 9.308316 ... 9.920602 17.031494 27.189178 34.895090 35.677321 28.118941 0.009270 0.075366 2559.593931 2053.894372
15 m00801.wav rock -212.298355 65.759850 45.182362 19.730082 11.805884 8.061574 4.434145 4.215858 ... 7.496245 14.080840 24.372299 29.930723 29.264457 19.750201 0.031043 0.108570 3203.015264 2947.109224
24 m00821.wav metal -58.720360 70.558182 -19.421354 73.593155 -27.765425 23.759441 -17.904169 21.222898 ... 3.810858 9.925774 14.813298 17.154393 16.498257 62.176253 0.025555 0.196540 2358.882414 3048.505206
41 m00850.wav disco -78.351181 80.074615 -9.167377 25.477053 -3.390544 16.809401 7.270706 1.818662 ... 3.839945 11.835582 16.459640 24.339468 26.110460 26.963877 0.040865 0.126389 2533.400412 2605.158779
11 m00867.wav rock -142.442062 116.238441 -32.190319 49.114605 -8.395415 22.872288 -18.311134 20.106487 ... 7.675207 11.516393 18.302119 25.070244 23.581773 56.706450 0.006455 0.097659 2068.224879 2006.009248
19 m00895.wav rock -26.406458 85.794289 -16.961445 35.260883 10.000134 10.668921 1.446997 4.710635 ... 4.893592 13.844285 19.658359 25.195343 25.510141 32.623636 0.027207 0.115493 2378.385172 2345.885737
30 m00996.wav jazz -235.678848 144.767654 -22.600695 62.560963 -9.326273 2.016869 -0.343650 2.016511 ... 4.571889 12.846922 19.813868 31.435092 32.057657 49.188174 0.001253 0.076529 1518.994776 1338.236233

50 rows × 60 columns

In [21]:
label_scaled = scaler(labeled)

label_scaled['genre'] = labeled['genre']

label_scaled = label_scaled.groupby('genre').agg('mean', numeric_only=True)
# Drop 'index' en 'sfreq' kolommen

display(label_scaled)
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
genre
blues 0.570724 0.603191 0.568935 0.512389 0.612034 0.713023 0.611799 0.789955 0.630810 0.617932 ... 0.696037 0.294558 0.595083 0.373805 0.533733 0.794573 0.513532 0.688573 0.437988 0.644441
classical 0.414737 0.380073 0.462212 0.307737 0.345461 0.437768 0.432761 0.529028 0.379019 0.562687 ... 0.221291 0.153904 0.387208 0.551137 0.488247 0.597860 0.573020 0.710912 0.470433 0.392179
country 0.528854 0.527735 0.502125 0.568212 0.442093 0.674517 0.573105 0.680076 0.633341 0.541148 ... 0.520322 0.291785 0.525166 0.222558 0.655681 0.693533 0.498192 0.733933 0.488153 0.533868
disco 0.539909 0.439278 0.620382 0.531246 0.503473 0.582386 0.525130 0.789353 0.553296 0.569495 ... 0.372165 0.192389 0.474003 0.454328 0.598727 0.728963 0.396498 0.617229 0.441749 0.352991
hiphop 0.510468 0.462382 0.379884 0.378835 0.367477 0.552595 0.435395 0.474046 0.386888 0.513529 ... 0.488845 0.244584 0.483315 0.347749 0.602565 0.637329 0.524570 0.590138 0.504995 0.434090
jazz 0.505609 0.470394 0.487221 0.416552 0.386641 0.413832 0.499157 0.746797 0.564836 0.518794 ... 0.543609 0.194101 0.492837 0.363454 0.562417 0.721323 0.449418 0.583019 0.335821 0.462383
metal 0.549765 0.504264 0.439388 0.459322 0.412351 0.556741 0.431805 0.456267 0.461138 0.397431 ... 0.342797 0.254174 0.515288 0.221448 0.647024 0.680236 0.644194 0.529290 0.368040 0.448331
pop 0.518554 0.508631 0.570681 0.543371 0.445909 0.539751 0.522993 0.705792 0.605422 0.568796 ... 0.738439 0.112546 0.516186 0.328336 0.630846 0.700289 0.446648 0.649486 0.415466 0.506472
reggae 0.536363 0.484059 0.481324 0.469088 0.555118 0.779702 0.515746 0.643189 0.522847 0.576208 ... 0.718651 0.100871 0.520251 0.323629 0.584332 0.674909 0.578131 0.577892 0.368364 0.427260
rock 0.383457 0.325879 0.421623 0.541028 0.499974 0.520385 0.349906 0.496622 0.391111 0.428603 ... 0.291130 0.195351 0.357569 0.407569 0.446922 0.680467 0.528873 0.732854 0.241475 0.297855

10 rows × 58 columns

In [22]:
def bereken_afstanden(genre_data, cluster_data):
    afstanden = []

    for genre_row in genre_data.iterrows():
        genre_name, genre_values = genre_row
        for cluster_row in cluster_data.iterrows():
            cluster_name, cluster_values = cluster_row
            distance = euclidean(genre_values, cluster_values)
            afstanden.append((genre_name, cluster_name, distance))

    return afstanden

afstanden = bereken_afstanden(label_scaled, unlabeled_cluster)

for genre_name, cluster_name, distance in afstanden:
    print(f"Afstand tussen {genre_name} en {cluster_name}: {distance}")
Afstand tussen blues en 0: 1.0170016124005132
Afstand tussen blues en 1: 2.1937363809353783
Afstand tussen blues en 2: 1.6445935562510356
Afstand tussen classical en 0: 2.115576584984316
Afstand tussen classical en 1: 1.126995881284671
Afstand tussen classical en 2: 1.5609093560555085
Afstand tussen country en 0: 1.5208883988424096
Afstand tussen country en 1: 1.7308268295016342
Afstand tussen country en 2: 1.515958030022638
Afstand tussen disco en 0: 1.6991841237131824
Afstand tussen disco en 1: 1.6767710966838931
Afstand tussen disco en 2: 1.3301505499765294
Afstand tussen hiphop en 0: 1.8854796656682073
Afstand tussen hiphop en 1: 1.2754201227992885
Afstand tussen hiphop en 2: 1.3846956845653426
Afstand tussen jazz en 0: 1.5660622016899195
Afstand tussen jazz en 1: 1.5318736189929327
Afstand tussen jazz en 2: 1.5465033832284947
Afstand tussen metal en 0: 2.1148332404711585
Afstand tussen metal en 1: 1.3690411500947441
Afstand tussen metal en 2: 1.397694757640086
Afstand tussen pop en 0: 1.122777838548499
Afstand tussen pop en 1: 1.930676190840679
Afstand tussen pop en 2: 1.8921279690022645
Afstand tussen reggae en 0: 1.1997686898611621
Afstand tussen reggae en 1: 1.8317876893179585
Afstand tussen reggae en 2: 1.7026037585260427
Afstand tussen rock en 0: 2.543455097958965
Afstand tussen rock en 1: 1.1754147711176846
Afstand tussen rock en 2: 1.6097773260146777
In [23]:
def bereken_manhattan_afstanden(genre_data, cluster_data):
    afstanden = []

    for genre_row in genre_data.iterrows():
        genre_name, genre_values = genre_row
        for cluster_row in cluster_data.iterrows():
            cluster_name, cluster_values = cluster_row
            distance = cityblock(genre_values, cluster_values)
            afstanden.append((genre_name, cluster_name, distance))

    return afstanden

manhattan_afstanden = bereken_manhattan_afstanden(label_scaled, unlabeled_cluster)

for genre_name, cluster_name, distance in manhattan_afstanden:
    print(f"Manhattan-afstand tussen {genre_name} en {cluster_name}: {distance}")
Manhattan-afstand tussen blues en 0: 6.197687605947785
Manhattan-afstand tussen blues en 1: 15.136279347730634
Manhattan-afstand tussen blues en 2: 10.064613083168368
Manhattan-afstand tussen classical en 0: 14.35685898646447
Manhattan-afstand tussen classical en 1: 7.48821444770054
Manhattan-afstand tussen classical en 2: 10.219852674924203
Manhattan-afstand tussen country en 0: 10.048769422698081
Manhattan-afstand tussen country en 1: 11.661693646645618
Manhattan-afstand tussen country en 2: 9.742798586207428
Manhattan-afstand tussen disco en 0: 10.99529378631154
Manhattan-afstand tussen disco en 1: 11.014624300158
Manhattan-afstand tussen disco en 2: 8.166065040840166
Manhattan-afstand tussen hiphop en 0: 12.619247319530173
Manhattan-afstand tussen hiphop en 1: 8.459395800905478
Manhattan-afstand tussen hiphop en 2: 8.692641531239193
Manhattan-afstand tussen jazz en 0: 10.339367587900833
Manhattan-afstand tussen jazz en 1: 10.07530220380237
Manhattan-afstand tussen jazz en 2: 9.523560213474553
Manhattan-afstand tussen metal en 0: 14.155633807717635
Manhattan-afstand tussen metal en 1: 8.725749618872847
Manhattan-afstand tussen metal en 2: 9.158480113059607
Manhattan-afstand tussen pop en 0: 7.122335811350672
Manhattan-afstand tussen pop en 1: 13.206176449965824
Manhattan-afstand tussen pop en 2: 11.444614980868499
Manhattan-afstand tussen reggae en 0: 7.984582411817846
Manhattan-afstand tussen reggae en 1: 12.339350610407957
Manhattan-afstand tussen reggae en 2: 10.42148998666098
Manhattan-afstand tussen rock en 0: 17.266382795487495
Manhattan-afstand tussen rock en 1: 7.546078980257016
Manhattan-afstand tussen rock en 2: 10.13951647202089

3.2: Bepalen van Genres¶

In [24]:
def bepaal_genres_voor_clusters(genre_data, cluster_data, afstanden_functie):
    toewijzingen = []

    for cluster_row in cluster_data.iterrows():
        cluster_name, cluster_values = cluster_row
        min_afstand = float('inf') 
        op_een_na_min_afstand = float('inf')  
        toegewezen_genre = None
        op_een_na_toegewezen_genre = None

        for genre_row in genre_data.iterrows():
            genre_name, genre_values = genre_row
            afstand = afstanden_functie(genre_values, cluster_values)

            if afstand < min_afstand:
                op_een_na_min_afstand = min_afstand
                op_een_na_toegewezen_genre = toegewezen_genre

                min_afstand = afstand
                toegewezen_genre = genre_name
            elif afstand < op_een_na_min_afstand:
                op_een_na_min_afstand = afstand
                op_een_na_toegewezen_genre = genre_name

        toewijzingen.append((cluster_name, toegewezen_genre, min_afstand, op_een_na_toegewezen_genre, op_een_na_min_afstand))

    return toewijzingen

toewijzingen_euclidisch = bepaal_genres_voor_clusters(label_scaled, unlabeled_cluster, euclidean)

toewijzingen_manhattan = bepaal_genres_voor_clusters(label_scaled, unlabeled_cluster, cityblock)

print("Toewijzingen op basis van Euclidische afstanden:")
for cluster_name, genre_name, afstand, op_een_na_genre, op_een_na_afstand in toewijzingen_euclidisch:
    print(f"Cluster {cluster_name} is toegewezen aan genre {genre_name} met een afstand {afstand}. Op één na dichtstbijzijnde genre: {op_een_na_genre} met een afstand {op_een_na_afstand}")

print("\nToewijzingen op basis van Manhattan-afstanden:")
for cluster_name, genre_name, afstand, op_een_na_genre, op_een_na_afstand in toewijzingen_manhattan:
    print(f"Cluster {cluster_name} is toegewezen aan genre {genre_name} met een afstand {afstand}. Op één na dichtstbijzijnde genre: {op_een_na_genre} met een afstand {op_een_na_afstand}")
Toewijzingen op basis van Euclidische afstanden:
Cluster 0 is toegewezen aan genre blues met een afstand 1.0170016124005132. Op één na dichtstbijzijnde genre: pop met een afstand 1.122777838548499
Cluster 1 is toegewezen aan genre classical met een afstand 1.126995881284671. Op één na dichtstbijzijnde genre: rock met een afstand 1.1754147711176846
Cluster 2 is toegewezen aan genre disco met een afstand 1.3301505499765294. Op één na dichtstbijzijnde genre: hiphop met een afstand 1.3846956845653426

Toewijzingen op basis van Manhattan-afstanden:
Cluster 0 is toegewezen aan genre blues met een afstand 6.197687605947785. Op één na dichtstbijzijnde genre: pop met een afstand 7.122335811350672
Cluster 1 is toegewezen aan genre classical met een afstand 7.48821444770054. Op één na dichtstbijzijnde genre: rock met een afstand 7.546078980257016
Cluster 2 is toegewezen aan genre disco met een afstand 8.166065040840166. Op één na dichtstbijzijnde genre: hiphop met een afstand 8.692641531239193
In [25]:
def plot_vis(data1, data2, x, y):


    plt.figure(figsize=(15, 6))

    sns.scatterplot(data=data1,
                    x=x,
                    y=y,
                    hue='cluster',
                    palette='viridis',
                    marker='o',  
                    legend='full')
    
    sns.scatterplot(data=data2,
                    x=x,
                    y=y,
                    hue='genre',
                    marker='D',  
                    legend='full')
    
    plt.title(f'Scatter Plot van {x} en {y} met Cluster- en Genrevergelijking')
    plt.xlabel(x)
    plt.ylabel(y)
    plt.legend(loc='upper right')
    plt.grid(True)
    plt.show()
In [26]:
unlabeled_cluster.reset_index(inplace=True)
display(unlabeled_cluster.head())
label_scaled.reset_index(inplace=True)
display(label_scaled.head())
cluster bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 0 0.523447 0.573262 0.556266 0.645412 0.599113 0.648855 0.666862 0.673735 0.685147 ... 0.883251 0.153669 0.552364 0.542640 0.341978 0.451951 0.441608 0.395708 0.413671 0.620927
1 1 0.234479 0.197888 0.223155 0.361775 0.196132 0.386166 0.210448 0.400807 0.288687 ... 0.337053 0.015374 0.197929 0.528294 0.626156 0.484505 0.415608 0.492764 0.430684 0.260223
2 2 0.826579 0.747882 0.424069 0.544913 0.483928 0.602510 0.586091 0.560396 0.512694 ... 0.067970 0.551744 0.782709 0.530890 0.376735 0.436866 0.419808 0.457613 0.418868 0.610555

3 rows × 59 columns

genre bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean ... spectral_contrast7_mean spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr
0 blues 0.570724 0.603191 0.568935 0.512389 0.612034 0.713023 0.611799 0.789955 0.630810 ... 0.696037 0.294558 0.595083 0.373805 0.533733 0.794573 0.513532 0.688573 0.437988 0.644441
1 classical 0.414737 0.380073 0.462212 0.307737 0.345461 0.437768 0.432761 0.529028 0.379019 ... 0.221291 0.153904 0.387208 0.551137 0.488247 0.597860 0.573020 0.710912 0.470433 0.392179
2 country 0.528854 0.527735 0.502125 0.568212 0.442093 0.674517 0.573105 0.680076 0.633341 ... 0.520322 0.291785 0.525166 0.222558 0.655681 0.693533 0.498192 0.733933 0.488153 0.533868
3 disco 0.539909 0.439278 0.620382 0.531246 0.503473 0.582386 0.525130 0.789353 0.553296 ... 0.372165 0.192389 0.474003 0.454328 0.598727 0.728963 0.396498 0.617229 0.441749 0.352991
4 hiphop 0.510468 0.462382 0.379884 0.378835 0.367477 0.552595 0.435395 0.474046 0.386888 ... 0.488845 0.244584 0.483315 0.347749 0.602565 0.637329 0.524570 0.590138 0.504995 0.434090

5 rows × 59 columns

In [27]:
clusters = [0]

genres = ['disco', 'pop']


x = 'rms_energy'
y = 'mfcc11_mean'

plot_vis(
    data1=unlabeled_data[unlabeled_data['cluster'].isin(clusters)],
    data2=label_scaled[label_scaled['genre'].isin(genres)],
    x=x,
    y=y
)
In [28]:
clusters = [1]

genres = ['metal', 'hiphop']

plot_vis(
    data1=unlabeled_data[unlabeled_data['cluster'].isin(clusters)],
    data2=label_scaled[label_scaled['genre'].isin(genres)],
    x=x,
    y=y
)
In [29]:
clusters = [2]

genres = ['jazz', 'classical']

x = 'rms_energy'
y = 'mfcc11_mean'

plot_vis(
    data1=unlabeled_data[unlabeled_data['cluster'].isin(clusters)],
    data2=label_scaled[label_scaled['genre'].isin(genres)],
    x=x,
    y=y
)

3.3: Principal Component Analysis¶

PCA Voorbeeld¶

Om een voorbeeld te geven van het uitvoeren van PCA hebben we eerst een dataset nodig. Voor het gemak maken wij gebruik van een kleine zelf verzonnen dataset. | | X1 | X2 | |-------|:-----:|:-----:| | 1 | 4 | 1 | | 2 | 2 | 3 | | 3 | 7 | 6 | | 4 | 5 | 4 | | 5 | 9 | 8 |

Stap 1: Standaardiseren¶

De eerste stap voor het uitvoeren van PCA is het standaardiseren van de bovenstaande dataset. Dit doen wij aan de hand van de formule voor de Z-score. $$ Z = \frac{{X - \mu}}{{\sigma}} $$

Door het toepassen van de Z-score krijgen wij de volgende gestandaardiseerde dataset:

xz1 xz2
1 -0.6325 -1.2649
2 -1.2649 0
3 0.6325 0.6325
4 0 -0.6325
5 1.8974 1.2649

Stap 2: Covariantiematrix¶

Vervolgen bereken wij de covariantie matrix van de gestandaardiseerde dataset. Dit doen wij aan de hand van de volgende formule:

$$ Cov(X_i, X_j) = \frac{\sum_{k=1}^{n}(X_i^k - \bar{X_i})(X_j^k - \bar{X_j})}{n-1} $$

Wanneer wij de bovenstaande formule toepassen krijgen wij het volgende matrix:

\begin{bmatrix} 6.75 & 0.5 \\ 0.5 & 5.0 \end{bmatrix}

Stap 3: Eigenwaarden en Eigenvectoren¶

In stap 3 berekenen wij de eigenwaarden en eigenvectoren.

Eigenwaarden: \begin{align*} \lambda_1 &= 1.9 \\ \lambda_2 &= 0.8 \end{align*}

Eigenvectoren: \begin{align*} v_1 &= \begin{bmatrix} 0.84 \\ 0.54 \end{bmatrix} \quad (\text{bij } \lambda_1) \\ v_2 &= \begin{bmatrix} -0.54 \\ 0.84 \end{bmatrix} \quad (\text{bij } \lambda_2) \end{align*}

Stap 4: Feature vector¶

Wanneer we kijken naar stap 3 zien we dat vector 1 de meeste significantie heeft, dit is dus de vector met de hoogste eigenwaarde. We selecteren daarom v1 als ons eerst principal component. v_1 &= \begin{bmatrix} 0.84 \\ 0.54 \end{bmatrix}

Stap 5: Projectie¶

Voor stap 5 gaan we de gestandaardiseerde gegevens vermenigvuldigen met de gekozen feature vector. Vervolgens krijgen de nieuwe dataset: $$[-0.6325, -1.2649, -1.2649, 0.6325, 0.6325] \cdot [0.84, 0.54] = [-1.6326, -1.2649, -0.0810, 2.6305] $$ Zo komen wij tot een nieuwe dataset die is gereduceerd tot één dimensie, de eerste principale component.

Nieuwe Dataset
1 -1.6326
2 -1.2649
3 -0.0810
4 2.6305

Voor het uitvoeren van een simpele PCA hebben wij de stappen gevolgd die beschreven staan in het volgende artikel. (Jaadi, 2023)

Nu dat wij een voorbeeld hebben gegeven van een simpele principal componenent analyse gaan wij dit nu uitvoeren op onze eigen dataset.

In [30]:
# Stap 1: Standaardiseren
#De data is eerder al gestandaardiseerd. Deze data kunnen we hier hergebruiken
scaled_df = data
In [31]:
# Stap 2: Covariantiematrix
# We maken een covariantiematrix aan de hand van no.cov
cov_matrix = np.cov(scaled_df, rowvar=False)
In [32]:
# Stap 3: Eigenwaarden en Eigenvectoren
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

# Nu sorteren wij de waardes
sorted_indices = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[sorted_indices]
eigenvectors = eigenvectors[:, sorted_indices]
In [33]:
# Aantal principal compponents kiezen
plt.plot(eigenvalues, marker='o')
plt.xlabel('Index')
plt.ylabel('Eigenvalue')
plt.title('Eigenvalues voor het bepalen van het aantal principal components')
plt.show()
In [34]:
# Stap 4: Feature vector
pca_num = 3
principal_components = eigenvectors[:, -pca_num:]
In [35]:
# Stap 5: Projectie
pca = PCA(n_components=pca_num)
data_pca = pca.fit_transform(scaled_df)
In [36]:
# Resultaten toevoegen aan dataframe
unlabeld_new = unlabeled_data.copy()
for i in range(pca_num):
    unlabeld_new[f'PC{i+1}'] = data_pca[:, i]
In [37]:
# Toon het resultaat
display(unlabeled_data.head())
bandwith centroid chromagram10_mean chromagram11_mean chromagram12_mean chromagram1_mean chromagram2_mean chromagram3_mean chromagram4_mean chromagram5_mean ... spectral_flatness spectral_rolloff tonnetz1_mean tonnetz2_mean tonnetz3_mean tonnetz4_mean tonnetz5_mean tonnetz6_mean zcr cluster
0 0.448700 0.463313 0.536488 0.723331 0.959317 0.760275 0.628028 0.688307 0.598168 0.561027 ... 0.075079 0.446538 0.601636 0.327193 0.433945 0.462291 0.286771 0.359017 0.485893 0
1 0.531218 0.660143 0.431423 0.385958 0.370729 0.728339 0.920078 0.984592 0.734350 0.421036 ... 0.220823 0.583361 0.565916 0.462638 0.419674 0.263680 0.141557 0.452145 0.803681 0
2 0.370122 0.372563 0.584292 0.383882 0.094401 0.600906 0.101713 0.107339 0.045195 0.394422 ... 0.017305 0.366321 0.539972 0.673517 0.074136 0.513645 0.213762 0.537025 0.504171 1
3 0.449617 0.502558 0.360861 0.606008 0.721541 0.650086 0.515102 0.610834 0.835636 1.000000 ... 0.073118 0.473765 0.552052 0.282639 0.611838 0.573084 0.650816 0.246407 0.518485 0
4 0.000000 0.022579 0.115212 0.573531 0.077500 0.000000 0.021968 0.402056 0.456909 0.020207 ... 0.000000 0.016254 0.234122 0.581224 0.558948 0.000000 0.204898 0.412068 0.112807 1

5 rows × 59 columns

In [38]:
#PCA toevoegen aan dataset
df_pca = scaled_df.copy()
for i in range(pca_num):
    df_pca[f'PC{i+1}'] = data_pca[:, i]
In [39]:
pca_df = df_pca[['PC1', 'PC2', 'PC3']]
In [40]:
#Getransformeerde dataset
display(pca_df.head())
PC1 PC2 PC3
0 1.836866 -0.018816 0.010516
1 1.996875 0.314686 0.013659
2 0.181995 -1.041242 1.209566
3 1.909274 0.041188 -0.277533
4 -0.783876 -1.630182 -0.864740
In [41]:
display(pca_df)
PC1 PC2 PC3
0 1.836866 -0.018816 0.010516
1 1.996875 0.314686 0.013659
2 0.181995 -1.041242 1.209566
3 1.909274 0.041188 -0.277533
4 -0.783876 -1.630182 -0.864740
... ... ... ...
100 -0.326987 -0.804086 -0.660464
101 -0.827754 1.464652 -0.120512
102 -0.867682 1.468914 0.062264
103 -0.416413 -1.447649 0.040551
104 -0.105994 -1.337032 -0.439203

105 rows × 3 columns

In [ ]:
 
In [ ]:
 
In [ ]:
 

3.4: Non-negative matrix factorization¶

In [42]:
scaler = MinMaxScaler()

columns_to_scale = unlabeled_df.columns[1:]

scaled_data = scaler.fit_transform(df[columns_to_scale])

nmf_scaled = pd.DataFrame(scaled_data, columns=columns_to_scale)
In [43]:
warnings.filterwarnings("ignore", category=UserWarning)

inertia = []
k = range(1, 11)

for i in k:
    model = KMeans(n_clusters=i, n_init=10)

    model.fit(nmf_scaled)

    inertia.append(model.inertia_)

plt.plot(k, inertia, '-x')
plt.xlabel('Aantal (k) clusters')
plt.ylabel('inertia')
plt.xticks(k)
plt.show()
In [44]:
model = NMF(n_components=3, init='random', random_state=42)

nmf_features = model.fit_transform(scaled_data)

NMF = pd.DataFrame(nmf_features,
                      columns=[f'component{i+1}' for i in range(3)],
                      index=scaled_df.index)
In [45]:
NMF = NMF.apply(pd.to_numeric, errors='coerce')

NMF.dropna(inplace=True)

kmeans = KMeans(n_clusters=3, n_init=10, random_state=42)

NMF['cluster'] = kmeans.fit_predict(NMF)

print(NMF['cluster'].value_counts())
cluster
0    40
1    39
2    26
Name: count, dtype: int64
In [46]:
import plotly.express as px


fig = px.scatter_3d(NMF, x='component1', y='component2', z='component3', color='cluster',
                    symbol='cluster', opacity=0.7, size_max=10)
fig.update_layout(scene=dict(xaxis_title='Component 1', yaxis_title='Component 2', zaxis_title='Component 3'))
fig.show()

# Bron: (3D, z.d.)

Non-negative Matrix Factorization¶

Voor gegeven matrix V van dimensie m × n, m = het aantal rijen n = het aantal kolommen is, NMF maakt de matrices W en H k = het aantal componenten W = m × k H = k × n. De benadering WH wordt gebruikt om V te benaderen.

NMF maakt W en H door de volgende functie te minimaliseren $ ||V - WH||^2_F $ Hierbij is ∣∣⋅∣∣F de reconstructiefout, wat aangeeft hoe goed de benadering WH tot V is. Optimalisatie-algoritme: De optimalisatie van W en H wordt uitgevoerd met methoden als multiplicatieve update-regels. De meest voorkomende update-regels zijn:

Update W: $ W_{ij} = W_{ij} \frac{(VH^T)_{ij}}{(WHH^T)_{ij}} $

Update H: $ H_{ij} = H_{ij} \frac{(W^TV)_{ij}}{(W^TWH)_{ij}} $

Hierbij zijn WT en HT de nieuwe matrices van W en H. De updates worden toegepast totdat de reconstructiefout convergeert. W als H bestaat uit niet-negatieve elementen. de componenten die door NMF worden gemaakt zijn interpreteer baar en lijken op de originele patronen

Rekenvoorbeeld¶

Hypothetische dataset schoolcijfers:

Matrix V heeft cijfers van 4 studenten met 5 vakken (4x5):

$ V = \begin{bmatrix} 7 & 6 & 8 & 9 & 5 \\ 6 & 7 & 8 & 9 & 4 \\ 8 & 7 & 9 & 9 & 6 \\ 9 & 8 & 9 & 8 & 7 \\ \end{bmatrix} $

We willen de vaardigheid van studenten en de moeilijkheid van het vak als componenten nemen, ( K = 2 ).

Willekeurige initialisatie:

$ W = \begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \\ 7 & 8 \\ \end{bmatrix} $

$ H = \begin{bmatrix} 1 & 2 & 3 & 4 & 5 \\ 6 & 7 & 8 & 9 & 10 \\ \end{bmatrix} $

Na het toepassen van de update-regels voor optimalisatie, convergeren ( W ) en ( H ) naar:

$ W = \begin{bmatrix} 0.9 & 0.8 \\ 0.7 & 0.6 \\ 1.2 & 1.3 \\ 0.8 & 0.7 \\ \end{bmatrix} $

$ H = \begin{bmatrix} 4.6 & 5.7 & 6.5 & 7.2 & 4.8 \\ 1.5 & 1.7 & 1.8 & 1.9 & 1.3 \\ \end{bmatrix} $

Nu kan ( V ) worden benaderd, de reconstructie van ( V ) is:

$ \begin{bmatrix} 6.26 & 6.47 & 7.01 & 7.64 & 5.1 \\ 6.08 & 6.33 & 6.84 & 7.45 & 4.95 \\ 7.41 & 7.65 & 8.28 & 9.02 & 6.02 \\ 8.4 & 8.68 & 9.36 & 10.18& 6.8 \\ \end{bmatrix} $

Nu zijn de componenten interpreteerbaar sinds er maar 2 zijn en we precies weten wat ze betekenen.

Hoofdstuk 4: Referentielijst¶

  • 3D. (z.d.). https://plotly.com/python/3d-scatter-plots/

  • Deruty, E. (2022, 15 december). Intuitive understanding of MFCCs - Emmanuel Deruty - Medium. Medium. https://medium.com/@derutycsl/intuitive-understanding-of-mfccs-836d36a1f779

  • Hathaway, R. B. (2005). TRANSDUCERS AND DATA ACQUISITION. In Elsevier eBooks (pp. 1–56). https://doi.org/10.1016/b978-075067719-6/50002-6

  • librosa.feature.spectral_contrast — librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/main/generated/librosa.feature.spectral_contrast.html

  • librosa.feature.tonnetz — librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/main/generated/librosa.feature.tonnetz.html

  • librosa.feature.spectral_rolloff — librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/main/generated/librosa.feature.spectral_rolloff.html

  • Wikipedia contributors. (2024b, februari 9). Chroma feature. Wikipedia. https://en.wikipedia.org/wiki/Chroma_feature

  • Verma, Y. (2021, 11 november). A Tutorial on Spectral Feature Extraction for Audio Analytics. Analytics India Magazine. https://analyticsindiamag.com/a-tutorial-on-spectral-feature-extraction-for-audio-analytics/

  • Wikipedia contributors. (2023, 1 december). Spectral flatness. Wikipedia. https://en.wikipedia.org/wiki/Spectral_flatness

  • Torres-García, A. A., Mendoza-Montoya, O., Molinas, M., Antelis, J. M., Moctezuma, L. A., & Hernández-Del-Toro, T. (2022). Pre-processing and feature extraction. In Elsevier eBooks (pp. 59–91). https://doi.org/10.1016/b978-0-12-820125-1.00014-2

  • Wikipedia contributors. (2024a, januari 5). Centroid. Wikipedia. https://en.wikipedia.org/wiki/Centroid

  • Wikipedia-bijdragers. (2023, 31 juli). Hoofdcomponentenanalyse. Wikipedia. Geraadpleegd op 21-1-2024 van https://nl.wikipedia.org/wiki/Hoofdcomponentenanalyse

  • Jaadi, Z. (2023, 29 maart). A Step-by-Step Explanation of Principal Component Analysis (PCA). Built In. Geraadpleegd op 21-1-2024 van https://builtin.com/data-science/step-step-explanation-principal-component-analysis

  • Beat and Tempo — Librosa 0.10.1 Documentation. (z.d.). https://librosa.org/doc/latest/beat.html

  • Filters — Librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/latest/filters.html#filter-bank-construction

  • Librosa.feature.spectral_centroid — Librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/latest/generated/librosa.feature.spectral_centroid.html

  • Librosa.feature.spectral_bandwidth — Librosa 0.10.1 Documentation. (z.d.). https://librosa.org/doc/latest/generated/librosa.feature.spectral_bandwidth.html

  • Librosa.feature.spectral_flatness — Librosa 0.10.1 Documentation. (z.d.). https://librosa.org/doc/latest/generated/librosa.feature.spectral_flatness.html

  • Librosa.feature.zero_crossing_rate — Librosa 0.10.1 Documentation. (z.d.). https://librosa.org/doc/latest/generated/librosa.feature.zero_crossing_rate.html

  • Librosa.feature.Tempogram — Librosa 0.10.1 documentation. (z.d.). https://librosa.org/doc/latest/generated/librosa.feature.tempogram.html

  • Scipy.spatial.distance.cityblock — SciPY v1.12.0 Manual. (z.d.). https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cityblock.html

  • Scipy.spatial.distance.euclidean — SciPY v1.12.0 manual. (z.d.). https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.euclidean.html